推荐
关注
TOP
Message

Vue3基础

1. Vuejs 的快速入门使用

1.1 下载及安装

常见的工具库:[react.js、vue.js、angular.js、jQuery]

官网:https://v3.cn.vuejs.org/
官方文档: https://v3.cn.vuejs.org/guide/introduction.html

Vue1.0 | 2.0 | 3.0

jQuery 和Vue 的定位是不一样的

jquery 的定位是获取元素和完成特效的
vuejs的定位是方便操作和控制数据和完成特效。

1.2 vue.js 的使用

vuejs 的使用提供了两种方式

  1. 基于脚本导入使用,下载vue.js,通过script标签 引入html网页。
  2. 基于项目的构建工具来进行使用。需要安装构建工具 自动生成一个独立项目,目前推荐的项目构建工具:vue-cli,vite

第一种方式 vuejs
官网下载:https://cdn.jsdelivr.net/npm/vue@next/dist/
github下载:https://github.com/vuejs/core/compare/v3.2.30...v3.2.31

image.png

第一个vue代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>01 简单使用</title>
    <script type="application/javascript" src="js/vue.js"></script>
</head>
<body>

<div id="app">
    <!-- {{message}} 表示对象里面data选项的对应数据输出到页面上  -->
    <!-- 在双标签中显示数据 需要通过{{ }} 来完成  -->
    <p>{{message}}</p>
    <p>{{num}}</p>
    <p>{{is_delete}}</p>

    <!--如果没有这个值 则不显示-->
<!--    <p>{{classmate}}</p>-->
</div>

<script type="application/javascript">
     // Vuejs的所有代码全部开始于Vue对象
     // Vue.createApp可以创建一个vue的实例对象  通过这个实例对象,我们可以操作制定的html的内容 数据 结构  特效 等等
     // 一个HTML 页面中 可以创建多个实例对象  但是这些实例对象的保存变量名 不能一样
     // 所以每次操作数据都要从创建vue实例对象开始
    const vm = Vue.createApp({
        // data 是将要展示到html标签元素的数据,一般会在data这里预先设置vue 要使用的变量数据,并给变量指定一个默认值
        // 当然 最终data 这里的数据都是来自服务器
        data() {
            return {
                message: "hello,python",
                num:200,
                is_delete:false
            }
        }
    }).mount("#app")

    //  保存mount方法的直接结果就是一个vue实例对象 里面可以直接访问data 返回结果的值
    // 只有挂载才能看到数据
    console.log(vm)
    console.log(vm.message) // 相当于直接获取data 选项里的is_delete
    console.log(vm.is_delete)
    console.log(vm.num)
</script>

</body>
</html>

下载 vueJsdevelop
方便调试Vue 下载的

1.3 VueMVVM思想

MVM 是Model-View-ViewModel的缩写,是一种基于前端开发的架构模式,是一种代码分工思想。

Model 指代的就是Vue对象的data数学里面的数据,这美丽的数据要显示到页面上。

View 指代的是 Vue中数据要显示的HTML页面,也称之为视图模板。

ViewModel 指代的就是Vue.js中我们编写代码的创建Vue实例对象Vm,他是Vue.js 的核心,负责连接View和Model,保证试图和数据的一致性。所以前面代码中,data选项里面的数据被显示总,p标签也就是Vm对象自动完成的。vm对象会时刻监控View和Model的变化,保证其一致性。有时候 这个特性也称之为双向绑定。

image.png

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>02 MVVM的思想</title>
    <script type="application/javascript" src="js/vue.js"></script>
</head>
<body>

<div id="app">
  <p>{{message}}</p>
  <input type="text" v-model="message">
  <p>{{num}}</p>
  <input type="number" v-model="num">
</div>


<script type="application/javascript">
  const vm = Vue.createApp({
    // data选项中声明的所有数据都会被vm对象 进行便利循环,复制给v名对象作为属性的,我们访问操作data里面的数据直接通过this来访问
    data() { // 保存数据,用于输出到 HTML页面的
      return {
        message:"hello",
        num:100
      }
    },
    mounted(){
        // 定时器 第一个参数是匿名函数,参数2 是一个定时时间,表示时隔多久 执行 第一个参数
        setInterval( // 会在Vue家在html完成以后 自动执行mounted中的代码
            ()=>{
                this.num+=1; // this 相当于python中的self
                console.log(this.vm);
            },1000
        )
    }
  }).mount("#app")
</script>

</body>
</html>

1.4Vue常用属性操作

可以在浏览器中在console.log 中通过vm对象,直接访问el和data属性,甚至可以访问到data里面的数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>04 vm 常用数学方法</title>
    <script type="application/javascript" src="js/vue.js"></script>
</head>
<body>

<div id="app">
  <p>{{num}}</p>
  <p>{{num}}</p>
  <p>{{num}}</p>
  <p ref="p3">{{num}}</p>
  <p>{{num}}</p>
  <p>{{num}}</p>
</div>


<script type="application/javascript">
  const vm = Vue.createApp({
    data() {
      return {
        num:100
      }
    },
  }).mount("#app")

  console.log(vm);
  console.log(vm.num);
  console.log(vm.$data); // vm对象要现实到页面中的数据
  console.log(vm.$data.num); // 访问data里面的数据
  console.log(vm.$el); // vm对象可以控制的范围
  console.log(vm.$el.parentElement);// #app标签本身
  console.log(vm.$refs); // vue中所有经过ref数学注册的标签元素
  console.log(vm.$refs.p3); // 这里表示html的模板视图中<p ref=p3>的这个元素
  vm.$refs.p3.style["backgroundColor"]="red";
  vm.$refs.p3.style.backgroundColor="yellow";

</script>

</body>
</html>

总结:

  1. 如果要输出data里面的数据作为双标签的内容 需要用到{
  2. 如果要输出data里面的数据作为表单元素的值,需要使用vuejs提供的元素属性v-model
  3. 使用v-model把data里面的数据显示到表单元素以后 一旦用户修改表单元素的值,则data里卖弄对应数据的值也会随之发生改变,甚至 页面中凡是使用了这个数据都会发生改变。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>04 显示数据</title>
    <script type="application/javascript" src="js/vue.js"></script>
</head>
<body>

<div id="app">
    <!--双括号内部可以调用js代码,但是必须以变量名开头-->
    <!-- message.toUpperCase(),把数据转成大写-->
    <!--message.spilt(""),把数据的字符案子副边界分割成单个字符组成的数据-->
    <!--array.reverse(),把数组的成员进行反转排序-->
    <!--array.join(""),巴蜀族的成员使用字符边界进行合并,变成自服从-->
    <p>{{message.toUpperCase().split("").reverse().join("")}}</p>

    <p>{{link}}</p>

    <p>&lt;a href=&quot;https://www.baidu.com&quot;&gt;百度&lt;/a&gt;</p>

    <p v-html="link"></p>

    <p>{{num}}</p>
    <p>{{num>100?'大于100':'小于或者等于100'}}</p>

    <p>{{num+0.8}}</p>

    <!--保留两位小数字-->
    <p>{{(num+0.8).toFixed(2)}}</p>

    <p>{{num+1000_000_100}}</p>

    <p>
        <input type="number" v-model="num">
    </p>

</div>


<script type="application/javascript">
  const vm= Vue.createApp({
      data(){
          return {
              num:10,
              message:"hello",
              link:'<a href="https://www.baidu.com">百度</a>'
          }
      }
  }).mount("#app")
</script>

</body>
</html>

2. 常用指令

指令(directives)是带有 v- 前缀的特殊属性 由vue提供的,每一个指令在vue中都有固定的作用

在vue中,提供了很多指令。常用的有:v-if,v-model,v-for等等

指令会在vm对象data属性的数据发生变化时,会同时改变元素中的其控制的内容或属性。

以为 vue的版本问题所有有两种写法

1.vue1.下写法		vue2.x/vue3.x用法
v-html				v-html
{{普通文本}}		{{普通文本}} v-text="普通文本"
v-bind:属性名		:属性
v-on:事件名		@事件名

2.1 属性操作

显示wifi 密码案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>05 属性操作</title>
    <script type="application/javascript" src="js/vue.js"></script>
</head>
<body>

<div id="app">
  <input :type="type" v-model="password"> <button @click="change">{{text}}</button>
</div>


<script type="application/javascript">
  const vm=Vue.createApp({
      data(){ //数据选项
          return{
              type:"text",
              password:"",
              text:"显示密码"
          }
      },
      methods:{ //方法选项
          change(){
              if (this.type==="text"){
                  this.type ="password";
                  this.text="显示密码"
              }else {
                  this.type="text";
                  this.text="隐藏密码"
              }
          }
      }

  }).mount("#app")
</script>

</body>
</html>

2.2 事件绑定

有两种操作手法 @事件名 和 v-on:事件名

1. 使用@事件名 来进行事件的绑定
语法:
<h1 @click="num++">
    {{num}}
</h1>

2. 绑定的事件的事件名,全都是js的事件名:
    @submit ---->	onsubmit
    @focus  ---->	onfocus
    @blur   ---->	onblur
    @click	---->	onclick
    ...

完成商城 购物车 增加减少计数器

  1. 给vue添加操作数据
  2. 在标签中使用指令调用操作数据的方法
 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>07 计数器</title>
    <script type="application/javascript" src="js/vue.js"></script>
</head>
<body>

<div id="app">
  <button @click="sub">-</button>
  <input type="text" size="2" v-model="num">
  <button @click="add">+</button>

</div>


<script type="application/javascript">
  const vm=Vue.createApp({
      data(){
          return {
              num:1, // 当前用户选择的商品数量
              max:10, // 库存
          }
      },
      methods:{
          sub(){
              // 减少数量
              if (this.num<=1) return; // return 表示终止
              this.num-=1
          },
          add(){
              // 增加数量
              if(this.num>=this.max) return;
              this.num+=1
          }
      }
  }).mount("#app")
</script>

</body>
</html>

2.3 操作样式

操作样式 本质上就是属性操作,使用【:】冒号

2.3.1 控制标签class类

格式: <h1: class"值">元素</h1> # 值可以是变量名,对象,对象的变量名,数组 数组变量名

示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>08 操作样式</title>
  <script type="application/javascript" src="js/vue.js"></script>

  <style>
    .p1{
        background: aqua;
        color: yellow;
    }

    .p2{
        background: blue;
        color: red;
    }
    .p3{
        border: 2px solid red;
    }

  </style>

</head>
<body>

<div id="app">
  <p :class="p1_cls" @click="p1_cls='p1'">1</p>

  <p :class="{p1:use_p1,p2:use_p2}" @click="use_p2=!use_p2">2</p> <!--最常用-->
  <p :class="cls" @click="cls.p2=!cls.p2">3</p><!--最常用-->
  <p :class="[cls,cls1]">4</p>

  
  <!-- 如果为真  则为p1 否则 则为p2 -->
  <p :class="condition?'p1':'p2'">5</p>
  <p class="p3" :class="condition?'p1':'p2'" @click="condition=!condition">6</p><!--最常用-->
</div>


<script>
  const vm=Vue.createApp({
      data(){
          return{
              p1_cls:"p2",
              use_p1:true,
              use_p2:false,
              cls:{
                  p1:true,
                  p2:false
              },
              cls1:{
                  p3:true
              },
              condition:false
          }
      }
  }).mount("#app")
</script>

</body>
</html>

总结:

给元素绑定class类名,最常用的就是第2种,3种,以及最后一种。
vue对象的data数据:
data{
myobj:{
complete:true,
uncomplete:false,
}
}

html 元素:

<div class="box" :class="myobj">222</div>

最终结果

<div class="box complete">222</div>

2.3.2 控制标签 style样式

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>09 操作样式-style</title>
  <script type="application/javascript" src="js/vue.js"></script>

</head>
<body>

<div id="app">
  <!-- 值是Json对象 对象直接写在style属性中 -->
  <div :style="{color:activeColor,fontSize:fontSize + 'px' }" @click="fontSize+=10">div1</div>
  <!-- 值是对象变量名,对象在data中进行声明 -->
  <div :style="styleObj">div2</div>
  <!-- 值是一个数组 -->
  <div :style="[style1,style2]">div3</div>

</div>


<script>
  const vm=Vue.createApp({
      data(){
          return{
              activeColor:"red",
              fontSize:30,
              styleObj:{
                  fontSize: "50px",
                  backgroundColor:"yellow"
              },
              style1:{
                  color:"red",
              },
              style2:{
                  backgroundColor: "blue"
              }

          }
      }
  }).mount("#app")
</script>

</body>
</html>

2.3.3 示例 vue选项卡

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>11 选项卡 完整</title>
  <script type="application/javascript" src="js/vue.js"></script>

  <style>

      p{
          padding: 0;
          margin: 0;
      }

    .title_list span{
        display: inline-block; /* 设置元素的显示模式:行级块状元素 */
        height: 60px;
        line-height: 60px;
        width: 120px;
        background-color: #bbb;
        text-align: center;
        cursor: pointer;
        margin: 1px;
    }

    .title_list .active{
        background-color: yellow;
    }

    .content_list .content{
        background-color: yellow;
        display: none;
        width: 365px;
        height: 280px;
    }
    .content_list .current{
        display: block;
    }

  </style>

</head>
<body>

<div id="app">
  <div class="title_list">
    <span :class="{active: key===0}" @click="key=0">国际新闻</span>
    <span :class="{active: key===1}" @click="key=1">国内新闻</span>
    <span :class="{active: key===2}" @click="key=2">外星新闻</span>
  </div>

  <div class="content_list">
    <div class="content " :class="{current:key===0}">
      <p>国际新闻列表</p>
      <p>国际新闻列表</p>
      <p>国际新闻列表</p>
      <p>国际新闻列表</p>
      <p>国际新闻列表</p>
    </div>
    <div class="content" :class="{current:key===1}">
      <p>国内新闻列表</p>
      <p>国内新闻列表</p>
      <p>国内新闻列表</p>
      <p>国内新闻列表</p>
      <p>国内新闻列表</p>
    </div>
    <div class="content" :class="{current:key===2}">
      <p>外星新闻列表</p>
      <p>外星新闻列表</p>
      <p>外星新闻列表</p>
      <p>外星新闻列表</p>
      <p>外星新闻列表</p>
    </div>
  </div>

</div>


<script>
  const vm=Vue.createApp({
      data(){
          return{
            key:0
          }
      }
  }).mount("#app")
</script>

</body>
</html>

运行效果

2.4 条件渲染指令

vue 提供了两个指令可以用于判断是否显示元素,分别是 v-if和v-show

2.4.1 v-if v-elif v-else

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>v-if</title>

  <script type="application/javascript" src="../js/vue.js"></script>

  <style>
    .l0{
        color: #000;
    }
    .l1{
        color: #fff999 ;
        background-color: black;
    }
    .l2{
        color: #aaaa00 ;
        background-color: black;
    }
    .l3{
        color: #fff333 ;
        background-color: black;
    }
  </style>

</head>
<body>
<div id="app">
  <!-- v-else 只能作为子语句 跟在上一个v-if后面  如果有多个else 后面的会覆盖上一个 -->
  <p v-if="username">欢迎回到xxx网站 {{username}}</p>
  <p v-else>欢迎访问xxx网站 尊贵的游客</p>

  <!-- vue对象会把最终的结果变成bool值 -->
  <p v-if="level===0" class="l0">您是会员vip</p>
  <p v-if="level===1" class="l1" >您是会员svip</p>
  <p v-if="level===2" class="l2" >您是会员mvip</p>
  <p v-if="level===3" class="l3" >您是会员msvip</p>
  <p v-else>您什么也不是</p>

</div>


<script>
  const vm = Vue.createApp({
      data(){
          return {
              username:"", // 用户名
              level:0 // 等级
          }
      }
  }).mount("#app")
</script>

</body>
</html>

2.4.2 v-show

用法和v-if大致一样 ,区别在于两点

  1. v-show后面不跟v-elif v-else
  2. v-show隐藏于元素时,使用的是display:none来隐藏的,而v-if是直接从HTML文档种移除元素[dom操作种的remove]
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>v-show</title>

  <script type="application/javascript" src="../js/vue.js"></script>

</head>
<body>
<div id="app">
  <p v-show="username">欢迎回到xxx网站 {{username}}</p>
  <p v-show="!username">欢迎回到xxx网站 {{username}}</p>

</div>


<script>
  const vm = Vue.createApp({
      data(){
          return {
              username:"", // 用户名
          }
      }
  }).mount("#app")
</script>

</body>
</html>

当页面使用了其他前端插件 这些插件也操作了和控制了Vue的内容范围重叠,则需要使用v-show

2.5 列表渲染指令

在vue中,可以通过v-for指令可以将一组数据渲染到页面中去,数据可以是数组 或者是对象

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>

  <script type="application/javascript" src="../js/vue.js"></script>

  <style>
    table{
        border-collapse: collapse;
        border: 1px solid red;
    }

    td,th{
        border: 1px solid red;
        padding: 10px 18px;
    }

  </style>

</head>
<body>
<div id="app">
  <table>
    <tr>
      <th>ID</th>
      <th>TITLE</th>
      <th>PRICE</th>
      <th>ATTR</th>
    </tr>


    <tr v-for="(book,key) in book_list">
      <td>{{key+1}}</td>
      <td>{{book.id}}</td>
      <td>{{book.title}}</td>
      <td>{{book.price}}</td>
<!--      <td>{{book.attr.publishDate}}</td>-->

      <td >
        <p v-for="(attr,key) in book.attr">
          {{attr}}
        </p>
      </td>

    </tr>

  </table>
</div>


<script>
  const vm = Vue.createApp({
      data(){
          return {
              book_list:[
                  {"id":11,"title":"三国演义","price":200, "attr":{"company":"人民邮电","publishDate":"2021-5-1"}},
                  {"id":22,"title":"水浒传","price":200, "attr":{"company":"中国邮电","publishDate":"2021-5-1"}},
                  {"id":33,"title":"红楼梦","price":200, "attr":{"company":"重庆邮电","publishDate":"2021-5-1"}},
                  {"id":44,"title":"西游记","price":200, "attr":{"company":"江苏邮电","publishDate":"2021-5-1"}},
              ]
          }
      }
  }).mount("#app")
</script>

</body>
</html>

3. vue对象提供的其他选项的api

3.1 过滤器

由于3.0 已经剔除了 过滤器 所以这里使用 vue2.6的版本

局部定义

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>

  <script type="application/javascript" src="../js/vue2.js"></script>
  <script src="../js/filters.js"></script>

</head>
<body>
<div id="app">
  <p>{{num | money}}</p>
  <p>{{num | money1}}</p>
</div>


<script>



  const vue= new Vue({ // 在vue3.0 后 该成 vue.createApp
      el:"#app", // 3.0之后移除了 el选项 替代成了mount
      data(){
          return {
              num:100.453
          }
      },
      method:{
        add(){
            this.num+=1
        },
        sub(){
            this.num-=1
        }
      },
      // 在vue对象内部定义一个filters选项
      filters:{
          money(data){
              return data.toFixed(2)+"元"
          }
      }
  })
</script>

</body>
</html>

全局过滤器 filters.js

// 定义全局范围的过滤器
Vue.filter("money1", function (v) {
    // 就是用来格式化这个数据的
    if (v === 0) {
        return v
    }
    return v.toFixed(2) + "元"
})

3.2 计算属性和侦听属性

计算属性
计算属性 是vue提供给开发这 用于编写代码时 计算出新的数据保存的变量,主要通过computed来声明

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>

  <script type="application/javascript" src="../js/vue.js"></script>

  <style>
      table {
          border-collapse: collapse;
          border: 1px solid red;
      }

      td, th {
          border: 1px solid red;
          padding: 10px 18px;
      }

  </style>
</head>
<body>
<div id="app">
  <table>
    <tr>
      <th>ID</th>
      <th>TITLE</th>
      <th>PRICE</th>
      <th>NUMBER</th>
      <th>TOTAL</th>
    </tr>

    <tr v-for="good in good_lists">
      <td>{{good.id}}</td>
      <td>{{good.title}}</td>
      <td>{{good.price}}</td>
      <td>
        <button @click="sub(good)">-</button>
        <input type="text" size="1" v-model="good.number">
        <button @click="add(good)">+</button>
      </td>

      <td>{{(good.number * good.price).toFixed(2)}}</td>
    </tr>

    <tr>
      <td colspan="3"></td>

      <td>总计:</td>

      <td>{{total_price}}</td>
    </tr>

    <tr>
      <td colspan="3"></td>

      <td>实付款:</td>

      <td>{{discount_price}}</td>
    </tr>


  </table>
</div>

<script>
    const vm = Vue.createApp({
        data() {
            return {
                good_lists: [
                    {"id": 11, "title": "三国演义", "price": 688.5, "number": 0},
                    {"id": 22, "title": "水浒传", "price": 200.11, "number": 0},
                    {"id": 33, "title": "红楼梦", "price": 150.23, "number": 0},
                    {"id": 44, "title": "西游记", "price": 110.53, "number": 0},
                ]
            }
        },
        // 计算属性 新增的属性 相当于在data中新声明了一个属性
        // 可以随着变量的变化而变化
        computed: {
            total_price() {
                let total = 0;
                for (let good of this.good_lists) {
                    total += good.price * good.number;
                }
                return total.toFixed(2);
            },
            discount_price(){
              let total=this.total_price
              if (total>=2000){
                  return (total-200).toFixed(2)
              }
                  return parseFloat(total).toFixed(2)
            },
        },
        methods: {
            add(good) {
                good.number += 1
            },
            sub(good) {
                if (good.number <= 0) return;
                good.number -= 1
            },

        }
    }).mount("#app")
</script>

</body>
</html>

侦听属性/监听属性

侦听属性 可以帮助我们监听data的某个数据的变化从而做相应 的自定义操作

侦听属性 是一个对象 它的键是要监听的对象或者变量,值是一般函数,当侦听的data数据发生变化时,会自定义的执行对应函数,当这个函数在被调用时,vue会传入两个形参, 第一个时变化的数据值, 第二个是变化后的数据值

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>

  <script type="application/javascript" src="../js/vue.js"></script>

  <style>
    input {
        outline: none;
        border: 1px solid #333;
    }
  </style>

</head>
<body>
<div id="app">
  用户名:<input type="text" ref="username" v-model="username"> <br><br>
  密码:<input type="password" ref="password" v-model="password"> <br><br>

</div>

<script>
    const vm = Vue.createApp({
        data() {
            return {
                username:"",
                password:""
            }
        },
        // 监听/侦听
        // 主要用来用于监听data中的数据或者computed的变量发生值的变化时 自动执行watch的函数
        watch:{
            username(){
                console.log(`username=${this.username}`)
                let length=this.username.length
                if (length>=6 && length<=16){
                    this.$refs.username.style.border="1px solid blue";
                }else {
                    this.$refs.username.style.border="1px solid red";
                }

            },
            // 修改前的值 和修改后的值
            password(new_value,old_value){
                if (/^\d+$/.test(this.password)){
                     this.$refs.password.style.border="1px solid red";
                }else {
                    this.$refs.username.style.border="1px solid blue";
                }
            }
        }
    }).mount("#app")
</script>

</body>
</html>

3.3 vue对象的生命周期

每个Vue对象在创建时 都要经过一系列的初始化过程。在这个过程中Vue.js会自动运行一些叫做生命周期的钩子函数,我们可以使用这些函数,在对象创建的不同阶段加上我们的代码,实现特定的功能
image.png

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>

  <script type="application/javascript" src="../js/vue.js"></script>

</head>
<body>
<div id="app">
  {{message}}
  <input type="text" v-model="num"> <p>{{num}}</p>
</div>

<script>
    const vm = Vue.createApp({
        data() {
            return {
                message: "hello,world",
                num:100
            }
        },
        beforeCreate(){
            // 自动钩子 初始化data选项之前
            console.log("》》》beforeCreate执行了。")
            console.log(this.$data)
            console.log(this.num) // 此时data还没有初始化 所以无法访问
        },
        created(){
            // 初始化data选项之后
            // 发送http请求到服务端获取数据 并补充到data选项中
            console.log("》》》created执行了,开发时一般在这里作数据的初始化,编写ajax或者其他网络请求。")
            console.log(this.$data)
            console.log(this.num) // 此时data还没有初始化 所以无法访问
        },
        beforeMount(){
            // 把data中的选项的数据赋值给html指令之前---->>>{{message}} 进行 html页面的修改
            console.log(">>>>> beforeMount 执行了");
            console.log(this.$el)
        },

        mounted(){
            console.log(">>>>>> mounted 执行了,开发一般在这里作页面的初始化,编写html页面/样式/其他的插件")
            // 把data中的数据替换到视图模板中
            console.log(this.$el.parentElement.innerHTML)
        },
        beforeUpdate(){
            // 修改数据前的钩子函数
            // 编写一些修改数据前的判断逻辑 例如数据的有效判断,操作人员的权限
            console.log(">>>>>> beforeUpdate 执行了 数据已经在data中变化 ,但是没有同步到 html代码中")
            console.log(this.$el.parentElement.innerHTML)
        },
        updated(){
          // data中的数据被改动 并同步到 HTML模板页面之后
            // 编写一些同步数据库的代码  发送http请求 通知服务器,数据被改动了
            console.log(">>>>>> updated 执行了")
            console.log(this.$el.parentElement.innerHTML)
        },
        beforeUnmount(){
            console.log(">>>>>>>beforeUnmount 被调用了")
        }
    }).mount("#app")
</script>

</body>
</html>

总结:

  1. 在vue使用的过程中,如果要初始化操作,把初始化操作的代码放到mounted中执行,
  2. mounted 阶段就是在vm对象以及把data数据 是先打页面以后,一般页面初始化使用,例如,用户访问页面加载成功后 就要执行 的ajax请求,
  3. 另一个就是created 这个阶段就是在vue对象创建以后,把ajax请求后端数据的代码放到created中

3.4 指令修饰符

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script type="application/javascript" src="../js/vue.js"></script>

</head>
<body>
<div id="app">
<!-- v-model.trim 用于去除字符串两边的空白字符 常用于密码 -->
  <input type="password" v-model.trim="password"> <br> <br>
<!--  v-model.lazy 用于减少触发watch侦听选项的频率 从原来的input事件侦听切换成了change 事件的监听  -->
  <input type="text" v-model.lazy="mobile"> <br> <br>
<!--  v-model.number 表示把变量作为数值进行转换 能转的都会转-->
  <input type="text" size="1" v-model.number="num">+500= <span>{{num+500}}</span> <br> <br>
</div>

<script>
    const vm = Vue.createApp({
        data() {
            return {
                password:"",
                mobile:"",
                num:""
            }
        },
        watch:{
            password(){
              console.log(this.password)
            },
            mobile(){
                // 发送http请求 验证手机号是否在服务端验证了
                console.log("发送http请求 验证手机号是否在服务端验证了")
                console.log(this.mobile)
            }
        }
    }).mount("#app")
</script>

</body>
</html>

v-model.trim 用于去除字符串两边的空白字符 常用于密码
v-model.lazy 用于减少触发watch侦听选项的频率 从原来的input事件侦听切换成了change 事件的监听
v-model.number 表示把变量作为数值进行转换 能转的都会转

事件修饰符使用 .stop | .prevent

.prevent 用于阻止页面跳转 用于a标签和form标签下的button

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>

  <script type="application/javascript" src="../js/vue.js"></script>

</head>
<body>
<div id="app">

  用户名:<input type="text" ref="username" v-model="username"> <br><br>
  密码:<input type="password" ref="password" v-model="password"> <br><br>
  <a href="http://www.baidu.com" @click.prevent="check_params">登陆</a>
</div>

<script>
    const vm = Vue.createApp({
        data() {
            return {
                username: "",
                password: ""
            }
        },
        methods: {
            check_params() {
                if (this.username !== "xiaoming") {
                    console.log("验证数据失败!!!!")
                    return;  // 阻止代码继续执行
                }else {
                    console.log("通过验证")
                }
            }
        }
    }).mount("#app")
</script>

</body>
</html>

.stop 用于阻止事件冒泡

冒泡:

js 的事件 出发过程分2阶段,

捕获阶段: 从外到内,父子元素之间,不断深入,查找触发事件的最开始的位置。

冒泡阶段: 从出发事件的元素开始 ,子级元素 往外层父级元素,不断外传,外传过程中,会顺便出发父级元素的同类事件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>

  <script type="application/javascript" src="../js/vue.js"></script>

  <style>
      body {
          margin: 0;
          padding: 0;
      }

      #app2 {
          position: fixed;
          top: 0;
          left: 0;
          background-color: rgba(0, 0, 0, 0.6);
          color: #fff;
          width: 68vw;
          padding-top: 30vh;
          padding-bottom: 30vh;
          padding-left: 32vw;
      }

      a {
          color: #fff;
          text-decoration: none;
      }

  </style>

</head>
<body>
<div id="app">
  <button @click="show_login_box=!show_login_box">登陆</button>
  <div id="app2" v-if="show_login_box" @click="show_login_box=false">
    <div @click.stop="">
      用户名:<input type="text" ref="username" v-model="username"> <br><br>
      密码:&nbsp;<input type="password" ref="password" v-model="password"> <br><br>
    </div>

    <a href="http://www.baidu.com" @click.prevent.stop="check_params">登陆</a>
    &nbsp;&nbsp;&nbsp;&nbsp;
    <a @click="show_login_box=false">取消登陆</a>
  </div>
</div>

<script>
    const vm = Vue.createApp({
        data() {
            return {
                username: "",
                password: "",
                show_login_box: false
            }
        },
        methods: {
            check_params() {
                if (this.username !== "xiaoming") {
                    console.log("验证数据失败!!!!")
                    return;  // 阻止代码继续执行
                } else {
                    console.log("通过验证")
                }
            }
        }
    }).mount("#app")
</script>

</body>
</html>

3.5 综合案例 todolist

例子

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>todolist</title>

  <script type="application/javascript" src="../js/vue.js"></script>

  <style>
      .list_con {
          width: 600px;
          margin: 50px auto 0;
      }

      .inputtxt {
          width: 550px;
          height: 30px;
          border: 1px solid #ccc;
          padding: 0;
          text-indent: 10px;
      }

      .inputbtn {
          width: 40px;
          height: 32px;
          padding: 0px;
          border: 1px solid #ccc;
      }

      .list {
          margin: 0;
          padding: 0;
          list-style: none;
          margin-top: 0px;
      }

      .list li {
          height: 40px;
          line-height: 40px;
          border-bottom: 1px solid #ccc;
      }

      .list li span {
          float: left;
      }

      .list li a {
          float: right;
          text-decoration: none;
          margin: 0 10px;
      }

  </style>
</head>
<body>

<div class="list_con" id="todolist">
  <h2>To Do List</h2>
  <input type="text" v-model="text" id="txt1" class="inputtxt">
  <input type="button" value="增加" @click="add_task" id="btn1" class="inputbtn" >

  <ul id="list" class="list" >
    <li v-for="(task,key) in task_list">
      <span>{{task}}</span>
      <a href="javascript:;" @click="up_task(key)" class="up">+</a>
      <a href="javascript:;" @click="down_task(key)" class="down">-</a>
      <a href="javascript:;" @click.prevent="del_task(key)" class="del">删除</a>

    </li>
  </ul>

</div>

<script>
    const vm = Vue.createApp({
        data() {
            return {
                text:"",
                task_list: [
                    "学习html",
                    "学习css",
                    "学习js",
                ]
            }
        },

        methods:{
            add_task(){

                // 添加任务计划
                if(this.text.length<1){
                    // 组织代码进行
                    return; // 组织任务继续继续进行
                }
                // this.task_list.push(this.text) // push 相当于python中的append
                this.task_list.unshift(this.text) // unshift 相当于python中的insert(0,成员)
                // 清空输入框
                this.text="";
            },
            del_task(key){
                // 删除数据
                // console.log(this.task_list[key])
                // this.task_list.splice(start,deleteCount) start 表示 操作开始的下标,deleteCount表示从从开始下标删除指定数量的成员,可以0——多个
                // this.task_list.splice(start,deleteCount,..new_item...t) new_item 表示新增成员 可以表示多个
                this.task_list.splice(key,1)
             },
            down_task(key){
                // 下移任务
                let del_task_list=this.task_list.splice(key,1);
                // console.log(del_task_list);
                this.task_list.splice(key+1,0,del_task_list[0]);

            },
            up_task(key){
                // 如果为最上面 则 直接终止
                if (key===0){
                    return;
                }
                // 下移任务
                let del_task_list=this.task_list.splice(key,1);
                // console.log(del_task_list);
                this.task_list.splice(key-1,0,del_task_list[0]);

            }
        }

    }).mount("#todolist")
</script>

</body>
</html>

使用存储

session 浏览器层面
// 存储数据
sessionStorage.setItem("username","root")
// 获取数据
sessionStorage.getItem("username")
// 删除数据
sessionStorage.removeItem("username")

本地层面
// 存储数据
localStorage.setItem("username","root")
// 获取数据
localStorage.getItem("username")
// 删除数据
localStorage.removeItem("username")

4. 组件化开发

4.1 脚本化组件

组件(Component)是自定义封装的功能。在前端开发过程中,经常出现多个网页的功能是重复的,
而且很多不同的网站之间,也存在同样的功能。

而在网页中实现一个功能,需要使用html定义功能的内容结构,使用css声明功能的外观样式,还要

使用js来定义功能的特效,因此就产生了把一个功能相关的HTML、css和javascript代码封装在一
起组成一个整体的代码块封装模式,我们称之为“组件”。

所以,组件就是一个html网页中的功能,一般就是一个标签,标签中有自己的内容结构,样式和特效。

这样,前端人员就可以在开发时,只需要书写一次代码,随处引入即可使用。

组件有两种:默认组件全局组件 和 单文件组件

脚本话开发 的罪案名: 单词小写+横杠

4.1.1 全局注册

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
  <script type="application/javascript" src="../js/vue.js"></script>

</head>
<body>

<div id="app">
<header-component></header-component>
</div>

<script>
// 组件声明 普通组件 脚本化
const headerComponent={
    data(){
        // 组件内部的数据
        return{
            num:100,
            title:"hello,world",
            nav_list:[
                "站点首页",
                "网站活动",
                "会员登陆",
            ]
        }
    },
    methods:{
        // 组件方法
        add(){
            this.num+=1
        }
},
    //模板代码
    template:
        `
          <ul>
          <li v-for="nav in this.nav_list">{{nav}}</li>
          </ul>
    <button @click="add">{{num}}</button>
    <button>{{title}}</button>
    `
}


const vm = Vue.createApp({
    data() {
        return undefined;
    }
//    组件名称 必须小写
}).component("header-component",headerComponent).mount("#app")

</script>

</body>
</html>

4.1.2 局部注册

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Title</title>
 <script type="application/javascript" src="../js/vue.js"></script>

</head>
<body>

<div id="app">
 <header-component></header-component>
</div>

<script>
   // 组件声明 普通组件 脚本化
   const headerComponent = {
       data() {
           // 组件内部的数据
           return {
               num: 100,
               title: "hello,world",
               nav_list: [
                   "站点首页",
                   "网站活动",
                   "会员登陆",
               ]
           }
       },
       methods: {
           // 组件方法
           add() {
               this.num += 1
           }
       },
       //模板代码
       template:
           `
             <ul>
             <li v-for="nav in this.nav_list">{{ nav }}</li>
             </ul>
             <button @click="add">{{ num }}</button>
             <button>{{ title }}</button>
           `
   }


   const vm = Vue.createApp({
       data() {
           return undefined;
       },
       components: {
           // "header-component": headerComponent
           // "headerComponent": headerComponent
       //简写

           headerComponent,
       }
   }).mount("#app")

</script>

</body>
</html>

组件之间的数据 互相隔离 互不打扰

5. 项目构建工具

在前端开发中,前端开发人员方便快速构建和管理项目,一般都会使用一些羡慕构建工具开辅助开发,而我们接下里啊要学习的单文件测试必须依赖于项目构建巩固,
一般有两种工具 vue-cli 和vite

5.1 vue自动化工具(vue-cli)

  • vue2.0 推荐使用 vue-cli
  • vue3.0 推荐使用vite2.0

一般情况 vue的单文件组件使用中,我们运行都会在vue-cli中进行开发,她可以帮我们吧单文件组件编译中js代码,所以需要安装好vue-cli工具

5.2 node.js

脚本语言需要一个解析器才能运行,JavaScript是脚本语言,在不同的位置有不一样的解析器,如写入html的js语言,浏览器是它的解析器角色。而对于需要独立运行的JS,nodejs就是一个解析器。

每一种解析器都是一个运行环境,不但允许js定义各种数据结构,进行各种计算,还允许js使用允许环境提供的内置对象和方法做一些事情。如运行在浏览器中的js的用途是操作DOM,浏览器就提供了document之类的内置对象。而运行在nodejs中的js的用途是操作磁盘文件或搭建http服务器,nodejs就相应提供了fs,http等内置对象。

能做什么?

NodeJS的作者说,他创造NodeJS的目的是为了实现高性能Web服务器,他首先看重的是事件机制和异步IO模型的优越性,而不是JS。但是他需要选择一种编程语言实现他的想法,这种编程语言不能自带IO功能,并且需要能良好支持事件机制。JS没有自带IO功能,天生就用于处理浏览器中的DOM事件,并且拥有一大群程序员,因此就成为了天然的选择。

如他所愿,NodeJS在服务端活跃起来,出现了大批基于NodeJS的Web服务。而另一方面,NodeJS让前端众如获神器,终于可以让自己的能力覆盖范围跳出浏览器窗口,更大批的前端工具如雨后春笋。

因此,对于前端而言,虽然不是人人都要拿NodeJS写一个服务器程序,但简单可至使用命令交互模式调试JS代码片段,复杂可至编写工具提升工作效率。

5.3 安装nvm 和 yarn

https://github.com/nvm-sh/nvm
安装nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash

修改hosts 才能安装成功
sudo vim /etc/hosts
199.232.96.133 raw.githubusercontent.com
重启网卡
systemctl restart NetworkManager.service
安装成功后

  1. 安装node 16.13.0
    nvm install v16.13.0
  2. 使用 16.13.0
    nvm use v16.13.0
  3. 查看安装环境
    nvm current
    nvm list
  4. 更换默认环境
    nvm alias default v16.13.0

安装不成功就使用
1.临时请使用导出用于抓取内容的镜像的非https版本:

exportNVM_NODEJS_ORG_MIRROR=http://nodejs.org/dist

第一种:若您运行curl $NVM_NODEJS_ORG_MIRROR
出现

curl: (77) error setting certificate verify locations:
  CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none

则考虑修改~/.nvm/nvm.sh
在函数nvm_download()里修改,加上curl -k $*

 if nvm_has "curl"; then
    curl -k $*  #新加的
  elif nvm_has "wget"; then
    # Emulate curl with wget
...
}

第二种:若您您或第一种没用,考虑和我一样粗暴解法,直接将if语句种的crul和wget换位置,如下(也就是先考虑wget了)

nvm_download() {
  local CURL_COMPRESSED_FLAG
  if nvm_has "wget"; then
        # Emulate curl with wget
    ARGS=$(nvm_echo "$@" | command sed -e 's/--progress-bar /--progress=bar /' \
                            -e 's/--compressed //' \
                            -e 's/--fail //' \
                            -e 's/-L //' \
                            -e 's/-I /--server-response /' \
                            -e 's/-s /-q /' \
                            -e 's/-sS /-nv /' \
                            -e 's/-o /-O /' \
                            -e 's/-C - /-c /')
    # shellcheck disable=SC2086
    eval wget $ARGS
  elif nvm_has "curl"; then
    if nvm_curl_use_compression; then
      CURL_COMPRESSED_FLAG="--compressed"
    fi
    curl --fail ${CURL_COMPRESSED_FLAG:-} -q "$@"
  fi
}

安装yarn

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list


sudo apt update && sudo apt install yarn

sudo apt-get remove yarn
sudo apt-get autoremove

npm install -g cnpm --registry=https://registry.npm.taobao.org //安装cnpm

5.4 安装vue-cli

要 初始化
yarn init
添加淘宝源
npm config set registry http://registry.npm.taobao.org/

npm install -g @vue/cli

yarn add global @vue-cli

速度不行可以考虑换源 npm

安装完毕 后 输入

5.4.1 常规安装

vue create vueproject

yarn 包管理 在国外 所以会让我们选择是否使用淘宝镜像
我们选择y即可

5.4.2 自定义安装

选择路由Router的路由模式 问我们是否使用历史状态管理模型,就是路由没有# 好

选择y

选择EsLint 配置型

选择ESLint格式化的时机 把两个都勾选上

恢复 node_moudulle

直接在目录下执行 yarn即可

5.4.3 项目目录结构

注意:如果项目缺少node_moudulle 直接 在目录下输入
yarn或者 npm update

  • 使用npm run serve 或者 yarn run serve 启动项目

5.4.4 项目流程图

整个项目是一个主文件index.html,inde.html会引入src文件夹中是main.js,main.js中会导入顶级单组文件组件App.vue ,app.vue 会通过组件嵌套或者路由来引用views页面组件,views组件 会根据开发的页面需要加载components文件的其他单文件组件

6. 单文件组件的使用

6.1 创建一个单文件组件

直接在components下面创建一个home.vue

在 组件中有三个标签

  • template 标签编写 html视图
  • script 标签操作vm对象
  • style 标签编写css样式
  • 组件文件名一般是大驼峰格式 每个首字母大写 以vue结尾

6.2 template 编写html代码

template内部有 且 只能有一个子标签 (3.0 不限制) 组件的所有代码必须包含在这个标签里

<template>
  <div>
    <h1>Home.vue</h1>
    <button @click="sub" class="sub">-</button>
    <input type="text" size="1px" v-model="num">
    <button @click="add" class="add">+</button>
  </div>
</template>

<script>
export default {
  name: "Home",
  data() {
    return {
      num: 0
    }
  },
  methods: {
    add() {
      this.num++;
    },
    sub(){
      this.num--;
    }
  },
  watch: {
    num() {
      if (this.num > 20) {
        this.num = 10;
      }
    }
  }
}
</script>

<style scoped>
  h1{
    color: red;
  }
</style>

6.3 运行 前端项目 (非终端运行)

如下图展示
image.png
接下来
image.png

然后单击右上角运行
即可完成运行

6.4 组件的嵌套

有时候开发Vue项目时,页面也可以算是一个大组件,同时页面也可以分成多个子组件,

例如我们就可以声明一个页面组件保存目录 src/views,并且提供两个页面组件,分别是Hello.vue, Register.vue作为页面组件,同时咋src/components创建按一个子组件Menu.vue

Home.vue

<template>
  <div>
    <Menu></Menu>
    <h1>Home.vue</h1>
    <button @click="sub" class="sub">-</button>
    <input type="text" size="1px" v-model="num">
    <button @click="add" class="add">+</button>
  </div>
</template>


<script>

import Menu from "@/components/Menu";

export default {
  name: "Home",
  data() {
    return {
      num: 0
    }
  },
  methods: {
    add() {
      this.num++;
    },
    sub(){
      this.num--;
    }
  },
  watch: {
    num() {
      if (this.num > 20) {
        this.num = 10;
      }
    }
  },

  components:{
    Menu,
  }
}
</script>

<style scoped>
  h1{
    color: red;
  }
</style>

然后 在app.vue组件上调用上面声明的home.vue 页面组件

<template>
  <Home></Home>
</template>

<script>
import Home from './views/Home.vue'

// 暴露语句
// 必须通过 export 导包过来
export default {
  name: 'App',
  data(){
    return {
      message:"hello,Python"
    }
  },
  components: {
    Home,
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

接着 Home.vue 页面需要菜单 则可以调用menu.vue

<template>
<div class="menu">
  <a href="">首页</a>
  <a href="">列表页</a>
  <a href="">会员中心</a>
</div>
</template>

<script>
export default {
  name: "Menu"
}
</script>

<style scoped>
  .menu{
    height: 80px;
    overflow: hidden;
  }

  .menu a{
    float: left;
    height: 80px;
    line-height: 80px;
    text-align: center;
    width: 140px;
    background-color: blue;
    color: #eee;
  }

</style>

使用不同的url 访问不同的页面

<template>
  <Home v-if="url==='/'"></Home>
  <Register v-if="url==='/register'"></Register>
</template>

<script>
import Home from './views/Home.vue'
import Register from "@/views/Register";


// 暴露语句
// 必须通过 export 导包过来
export default {
  name: 'App',
  data(){
    return {
      url:location.pathname,
      message:"hello,Python"
    }
  },
  components: {
    Register,
    Home,
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

后续会使用v-router 来完成路由显示

此时 整个目录的嵌套结构应该是:

6.5 组件之间的数据传递

父组件的数据传递给子组件
把父组件的数据传递给子组件,可以通过props来进行数据传递
传递数据三个步骤

  1. 在父组件中,调用子组件的组名处,使用属性值的方式向下传递
<template>
  <div>
    <Menu :index="0" msg="首页"></Menu>
    <!--  父组件要发送数据给子组件,通过组件的属性传递,属性名就是将来的变量名,传递数据如果是个变量,则需要在左边加上英文冒号  -->
    <h1>Home.vue</h1>
    <button @click="sub" class="sub">-</button>
    <input type="text" size="1px" v-model="num">
    <button @click="add" class="add">+</button>
  </div>
</template>


<script>

import Menu from "@/components/Menu";

export default {
  name: "Home",
  data() {
    return {
      num: 0
    }
  },
  methods: {
    add() {
      this.num++;
    },
    sub(){
      this.num--;
    }
  },
  watch: {
    num() {
      if (this.num > 20) {
        this.num = 10;
      }
    }
  },

  components:{
    Menu,
  }
}
</script>

<style scoped>
  h1{
    color: red;
  }
</style>

2.在子组件Menu.vue中接受上面父组件传递的数据,需要在script中,使用props属性接受,写法有两种

<script>
export default {
  name: "Menu",
  props:["index","msg"] // 数组对象写法 必须指定数据类型
  /*
  props:{
    index:Number, // 变量: 数据类型
    msg:String,
  }
  */
}
</script>

props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // 或任何其他构造函数
}

传递效果

注意事项

  1. 传递数据是变量,则需要在属性左边添加英文冒号,传递数据是变量这种数据称之为:“动态数据传输”,父组件数据改动的时候,子组件中被随之改动,传递数据不是变量,这种数据称之为:“静态数据传递”
  2. 父组件中修改了数据,在子组件中会被同步修改,但是来自props的数据,在子组件是不能修改的,因为不是data中声明的。在开发时遇到这种情况 也被称为“单向数据流”

子组件传递数据给父组件

在子组件中,通过this.$emit()来声明一个自定义的信号(vue中称之为自定义事件),父组件中针对这个信号(自定义事件)在调用子组件的组件名处进行@监听

menu.vue

<template>
  {{msg}}

  <button @click="myAdd">{{num}}</button>

<div class="menu">
  <a href="/" :class="{current:index===0}">站点首页</a>
  <a href="/register" :class="{current:index===1}">登陆/注册</a>
  <a href="" :class="{current:index===2}">会员中心</a>
</div>
</template>

<script>
export default {
  name: "Menu",
  props:{
    index:Number, // 变量: 数据类型
    msg:String,
    num:{
      type:Number,
      default:10
    }
    /*

    type:数据类型 Number   String   Boolean     Array 数组      Object 对象   函数 function   异步对象Promise
    default: 默认值 父组件没有对数据进行传递数据 默认使用该值

     */
  },
  methods:{
    myAdd(){
      // 往父组件 传递一个事件信号
      console.log("子组件被点击了")
      // this.$emit("add_num")
      // this.$emit("事件名","参数1","参数2","...")

      this.$emit("son_add",2)

    }
  }
}
</script>

<style scoped>
  .menu{
    height: 80px;
    overflow: hidden;
  }

  .menu a{
    float: left;
    height: 80px;
    line-height: 80px;
    text-align: center;
    width: 140px;
    background-color: blue;
    color: #eee;
  }

  .menu a.current{
    background-color: cadetblue;

  }

</style>

和子组件中this.$emit("自定义事件名称")对应,父组件中声明一个相同名称的事件属性
home.vue

<template>
  <div>
    <Menu :index="0" msg="首页" :num="num" @son_add="son_add"></Menu>
    <!--  父组件要发送数据给子组件,通过组件的属性传递,属性名就是将来的变量名,传递数据如果是个变量,则需要在左边加上英文冒号  -->
    <h1>Home.vue</h1>
    <button @click="sub" class="sub">-</button>
    <input type="text" size="1px" v-model="num">
    <button @click="add" class="add">+</button>
  </div>
</template>


<script>

import Menu from "@/components/Menu";

export default {
  name: "Home",
  data() {
    return {
      num: 0
    }
  },
  methods: {
    add() {
      this.num++;
    },
    sub(){
      this.num--;
    },
    son_add(args1){
      console.log(`父组件接受的变量${args1}`)
      this.num+=args1
    }
  },
  watch: {
    num() {
      if (this.num > 20) {
        this.num = 10;
      }
    }
  },

  components:{
    Menu,
  }
}
</script>

<style scoped>
  h1{
    color: red;
  }
</style>

附加说明

为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证要求的对象,而不是一个字符串数组。例如:

app.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 值会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组的默认值必须从一个工厂函数返回
      default() {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator(value) {
        // 这个值必须与下列字符串中的其中一个相匹配
        return ['success', 'warning', 'danger'].includes(value)
      }
    },
    // 具有默认值的函数
    propG: {
      type: Function,
      // 与对象或数组的默认值不同,这不是一个工厂函数——这是一个用作默认值的函数
      default() {
        return 'Default function'
      }
    }
  }
})

7.组件中使用axios获取来自服务端的数据

7.1 原生ajax 获取天气

ajax
async JavaScript and xml 是一种异步页面刷新技术(局部刷新技术),默认情况下由浏览器中原生的JavaScript提供了一个XMLHttpRequest对象允许我们在用户不知情的情况下啊 发送 http请求,访问服务端获取数据。
ajax 原生代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax</title>

    <style>
        .weather table{
            width: 800px;
            border-collapse: collapse;
        }
        .weather table td{
            width: 120px;
            height: 40px;
            line-height: 40px;
            border: 1px solid red;
            text-align: center;
        }

    </style>
</head>
<body>
    <input type="text" id="city" value="苏州">
    <button onclick="get_weather()"> 获取数据 </button>

    <div class="weather">
        <table>
            <tr>
                <td>data</td>
                <td>fengxiang——fengli</td>
                <td>low——high</td>
                <td>type</td>
            </tr>
        </table>
    </div>

    <script>
        // 原生ajax 获取数据
        let city =document.querySelector("#city");
        let table = document.querySelector(".weather table");
        // 先保存页面原有的样子
        let html = table.innerHTML;
        const get_weather=()=>{
            console.log(`${city.value}的天气预报`);
            // 实例化一个ajax对象
            let xhr = new XMLHttpRequest()
            console.log(xhr.readyState) // 0 ajax 实例化
            // 设置http请求信息
            xhr.open("get",`http://wthrcdn.etouch.cn/weather_mini?city=${city.value}`)
            // xhr.setRequestHeader() // 设置请求头信息

            // 发送http请求
            xhr.send() // 请求体 username=123&password=123
            console.log(xhr.readyState) // 1 构建完成http请求 并且发送
            // 通过ajax的监听事件,监听ajax是否已经获取到服务端返回的数据结果了
            xhr.onreadystatechange=()=>{
                // 当状态发生变化时,执行该函数
                // console.log(xhr.readyState) // 2:http请求已经成功被服务端接受, 3:http请求已经在服务端处理中 4: http请求被服务端处理完成后并返回给客户端ajax
                if (xhr.readyState===4){ // http请求被服务端处理完成后并返回给客户端ajax
                    if (xhr.status===200){ // 判断http请求是否相应成功
                        // 接受结果
                        // console.log(xhr.response) // 得到的数据是文本格式的 还需要转换格式
                        let data=JSON.parse(xhr.response);
                        // console.log(data.data.forecast);

                        let content="";

                        let fengli="";

                        for (let item of data.data.forecast){
                            // console.log(item);
                            fengli = item.fengli.slice(9,11)
                            content+=`
                             <tr>
                                <td>${item.date}</td>
                                <td>${item.fengxiang}——${fengli}</td>
                                <td>${item.low}——${item.high}</td>
                                <td>${item.type}</td>
                            </tr>

                            `
                        }
                        table.innerHTML=html+content;

                    }
                }
            }
        }

    </script>


</body>
</html>

7.2 vue axios 获取天气

默认情况下,vue的项目中并没有对ajax支持的,所以我们需要下载axios包
在项目根目录西安使用npm或者yarn安装包

npm install axios

yarn add axios

接着再src/utils目录下 创建一个http.js脚本,导入axios并通过create方法实例化一个http请求对象,这样才能再组件中使用。

import axios from "axios"; // 要导入安装的宝 ,直接写包名即可,不需要写路径


// 实例化
const http = axios.create({
    baseURL:'http://wthrcdn.etouch.cn/', // 请求的公共路径,一般写服务器默认的api地址,,这个地质在具体使用的时候覆盖
    timeout:8000,                        // 最大的请求超时,超过这个时间就报错,有文件上传的站点 不需要设置
    headers:{
        "X-Custom-Header":"foobar" // 默认的自定义请求头,一般工作中这里填写隐藏了客户端身份的字段
    }
})

// 暴露变量
export default http;

Register.vue 代码

<template>
  <Menu :index="1" msg="注册"></Menu>
  <h1>注册页面</h1>


  <input type="text" v-model="city">
  <button @click="get_weather"> 获取数据</button>

  <div class="weather">
    <table>
      <tr>
        <td>data</td>
        <td>fengxiang——fengli</td>
        <td>low——high</td>
        <td>type</td>
      </tr>
      <tr v-for="item in this.weathers">
        <td>{{item.date}}</td>
        <td>{{item.fengxiang}}——{{item.fengli}}</td>
        <td>{{item.low}}——{{item.high}}</td>
        <td>{{item.type}}</td>
      </tr>
    </table>
  </div>


</template>

<script>
import Menu from "@/components/Menu";
import http from "@/utils/http";

export default {
  name: "Register",
  components: {
    Menu,
    http,
  },
  data() {
    return {
      city: "苏州",
      // 声明一个变量存放天气
      weathers: []
    }
  },
  methods: {
    get_weather() {
      // 获取指定城市的天气
      // http.get(`http://wthrcdn.etouch.cn/weather_mini?city=${this.city}`).then(response => {
      http.get(`http://wthrcdn.etouch.cn/weather_mini`,{
        params:{
          city:this.city
        },
        headers:{
          // 自定义请求头
        }
      }).then(response => {
        if (response.status === 200) {
          // console.log(response?.data?.data?.forecast);
          let weatherData = response?.data?.data?.forecast
          if (weatherData) {
            this.weathers = weatherData;
          }
        }
      }).catch(error=>{
        // ajax 在请求或者相应处理过程中出现异常,则自动调用这里
        console.log(error)// 打印错误信息
        console.log(error.response)// 打印来自服务端的报错
      })
    }
  }
}
</script>

<style scoped>
.weather table {
  width: 800px;
  border-collapse: collapse;
}

.weather table td {
  width: 120px;
  height: 40px;
  line-height: 40px;
  border: 1px solid red;
  text-align: center;
}

</style>

App.vue

<template>
  <Home v-if="url==='/'"></Home>
  <Register v-if="url==='/register'"></Register>
</template>

<script>
import Home from './views/Home.vue'
import Register from "@/views/Register";


// 暴露语句
// 必须通过 export 导包过来
export default {
  name: 'App',
  data(){
    return {
      url:location.pathname,
      message:"hello,Python"
    }
  },
  components: {
    Register,
    Home,
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

7.3 与django drf对接

<template>
  <Menu :index="1" msg="注册"></Menu>
  <h1>注册页面</h1>
  <button @click="user_register"> 用户注册</button>
  <hr>

  <input type="text" v-model="city">
  <button @click="get_weather"> 获取数据</button>

  <div class="weather">
    <table>
      <tr>
        <td>data</td>
        <td>fengxiang——fengli</td>
        <td>low——high</td>
        <td>type</td>
      </tr>
      <tr v-for="(item,key) in this.weathers" :key="key">
        <td>{{ item.date }}</td>
        <td>{{ item.fengxiang }}——{{ item.fengli }}</td>
        <td>{{ item.low }}——{{ item.high }}</td>
        <td>{{ item.type }}</td>
      </tr>
    </table>
  </div>


</template>

<script>
import Menu from "@/components/Menu";
import http from "@/utils/http";

export default {
  // eslint-disable-next-line vue/multi-word-component-names
  "name": "Register",
  "components": {
    Menu,
    // eslint-disable-next-line vue/no-unused-components
    http,
  },
  "data"() {
    return {
      "city": "苏州",
      // 声明一个变量存放天气
      "weathers": []
    }
  },
  "methods": {
    "get_weather"() {
      // 获取指定城市的天气
      // http.get(`http://wthrcdn.etouch.cn/weather_mini?city=${this.city}`).then(response => {
      http.get(`http://wthrcdn.etouch.cn/weather_mini`, {
        "params": {
          "city": this.city
        },
        "headers": {
          // 自定义请求头
        }
      }).then(response => {
        if (response.status === 200) {
          // console.log(response?.data?.data?.forecast);
          let weatherData = response?.data?.data?.forecast
          if (weatherData) {
            this.weathers = weatherData;
          }
        }
      }).catch(error => {
        // ajax 在请求或者相应处理过程中出现异常,则自动调用这里
        console.log(error)// 打印错误信息
        console.log(error.response)// 打印来自服务端的报错
      })
    },

    "user_register"() {
      // 使用post请求数据 也可以使用put 和patch来获取数据参数与配置与post相同
      // 参数1 url
      // 参数2 data 请求体数据
      // 参数3 config 与get中相同
      http.post(`http://127.0.0.1:8000/api/stu/`, {
        "name": "小王八",
        "gender": false,
        "age": 25,
        "classNum": "301",
        "description": "巴拉巴拉"
      }, {
        "headers": {
          // 自定义请求头
        }
      }).then(response=>{
        console.log(response.data)
      }).catch(error=>{
        console.log(error)
        console.log(error.response)
      })
    }
  }
}
</script>

<style scoped>
.weather table {
  width: 800px;
  border-collapse: collapse;
}

.weather table td {
  width: 120px;
  height: 40px;
  line-height: 40px;
  border: 1px solid red;
  text-align: center;
}

</style>

当你访问 drf 的接口时会报错

使用的时候,因为本质上来说,我们使用的时axios发起http请求,本质上还是JavaScript发起的ajax,所以也会和普通的JavaScript收到同源策略的影响

所谓的同源策略 ,实际上时浏览器为了保护客户端用户再服务端中的数据的安全性所设置的一种网络安全机制,安全策略,
同源策略,可以禁止不同源下的客户端再js再没有得到服务端授权的情况下操作或者调用服务器的数据。
所谓的同源,实际上就是判断通信的客户端服务端,是否处于同一个协议,同一个域名ip,同一个端口下,如果三者相同,则服务端和客户端属于同源。则同源策略不会拦截客户端的js请求服务端数据,如果三者中任意有个信息不一致,则数据属于不同源,不同源的客户端js无法访问和操作服务端的数据。

解决同源策略的方式有3中,jsonp,服务端代理,CORS服务端授权。
CORS: 跨域资源共享

一、跨域的原因:浏览器的同源策略

在这里插入图片描述

上述情况就是出现了跨域问题。

二、什么是同源策略呢?

 出于安全考虑,默认在网页中,使用JS发送请求,请求的服务器和当前所在网页的服务器必须一样,即两个请求地址的 协议、域名、端口必须一致。

三、为什么会跨域呢?

 目前,我们大量的前后端分离项目(后端有自己的服务器,前端有自己的服务器),各自有各自的服务器,互相交互时必出现跨域问题。

四、跨域的解决方案:(百度有9种,常用的有3种)

1、CORS:最推荐----只需要在服务器端添加白名单即可,允许其他来源的访问

这里由于我后端使用的node,所以下面代码是在node环境下使用CORS解决跨域的方案:

(1)下载第三方模块:npm install cors --save

(2)在你的app.js中加上下面代码(一定要放在接口的前面)

var cors = require('cors');
app.use(cors({
    origin:['http://localhost:8083'],  // 要设置白名单的 地址
    methods:['GET','POST'],  // 请求方式
    alloweHeaders:['Conten-Type', 'Authorization']
}));

2、proxy:代理方案----次推荐,这是在前端解决跨域的问题

 适合服务器代码不可修改的场景

    利用服务器之间的交互没有跨域限制的特征

Vue中解决跨域问题

 Vue脚手架生成的项目包,自带一个服务器,支持快速设置代理服务器,临时解决跨域问题,不适合上线。

(1)在项目的根目录下(和README.md同级)创建固定名称的配置文件:vue.config.js

(2)在vue.config.js写以下代码(注意修改 target的属性的值 )

// 配置文件vue.config.js 是固定名
// 必须放在项目根目录下,与src同级别
module.exports = {
    //devServer:开发服务器配置项
    devServer: {
        // 小写的 procy
        proxy: {
            // 自定义代理名,请求时使用
            '/bili': {
                //target:代表要跨域到哪(dy 要映射到 trget 的域名)
                target: 'https://api.bilibili.com', //注意:结尾不要加 /
                changeOrigin: true, //关键点:代表需要跨域
                // 路径重写
                pathRewrite: {
                    // ^:正则的字符串开头
                    // 把开头的 /bili 替换成 '',即删除开头的 /bili
                    // 如果不把 /bili 替换掉,那么请求地址中就会多一个 "/bili" ,请求路径就错了
                    '^/bili': ''
                }
            }
            
        }
    }
}

(3)修改我们的请求地址,用 /bili 替换target后面的代码

 如:你要请求的地址是:‘https://api.bilibili.com/index.php’

            你要改成: ‘/bili/index.php’

(4)重启服务器(只要修改配置相关文件都需要重启服务器)

3、jsonp:前后端一起使用,现在已经基本不用了,所以这里简单简单介绍一下

1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;

2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有"src"这个属性的标签都拥有跨域的能力,比如、、);

3、所以jsonp能解决跨域的原理就是:利用的“src”属性发送请求,就不会受跨域问题的影响了。

7.4 vue-router 路由守卫

vue-router路由主要是三种

  • to,要进入的目标对象
  • from,当前导航要离开的路由
  • next fn 必须要执行,主要要使用她话跳转到一个新的地址

1.全局路由

2. 路由独享的守卫

3. 写在组件里面的

- 进入组件之前 beforeRouterEnter
- 组件被利用时 beforeRouterUpdate
- 离开组件之后 beforeRouterLeave

8.vite

8.1 vite 的安装

npm init @vitejs/app

依次选择 即安装成功
执行 yarn 完成依赖的安装

最后运行
npm run dev
或者
vite即可开启服务器

安装jsx 完成对vite的使用

yarn add @vitejs/plugin-vue-jsx

然后需要在vite.config.js中引入这个包

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';

  // https://vitejs.dev/config/
  export default defineConfig({
    plugins: [vue(),vueJsx()]
  })

并且 可以在vite.config.js 设置服务器地址和端口

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [vue(),vueJsx()],
    server:{
      // 配置服务器地质
      host:'0.0.0.0',
      port:5005
    }
  })

8.2 基础使用

当前vite搭建的项目属于组合api 直接使用组合api编写

安装与配置 vue-router
yarn add vue-router@4

初始化路由组件

import {createRouter,createWebHistory} from "vue-router";


const router=createRouter({
    history:createWebHistory(),
    routes:[
        {
            meta:{
                title:'wifi密码'
            },

            path:'/wifi',
            name:'wifi',
            component:import('../views/wifi.vue')
        }
    ]
})

export default router


router.beforeEach((to)=>{
    document.title=to.meta.title;
})

main.js

import { createApp } from 'vue'
import App from "./App.vue";
import router from "./router";



createApp(App).use(router).mount('#app')

例子 wifi密码

src/views/wifi.vue

<template>
  <h1>wifi例子</h1>

  <input :type="type" v-model="password">
  <button @click="change">{{text}}</button>
  <div><span>验证结果{{check}}</span></div>
</template>

<script setup>
  import {ref,watch} from "vue"


  let type =ref("password")
  let text=ref("显示密码")

  const change = () =>{
    if (type.value==="text"){
      type.value="password";
      text.value="显示密码";
    }else {
      type.value="text";
      text.value="隐藏密码";
    }
  }

  let password = ref("");
  let check = ref(false); // 密码是否通过
  // 监听数据
  watch(
      password,
      ()=>{
        let pl=password.value.length
        if(pl<6 || pl>16){
          check.value=false
        }else {
          check.value=true
        }
      }
  )

</script>


<style scoped>
input{
  border: 1px solid #333;
  outline: #333333;
}
</style>

index.js

import {createRouter,createWebHistory} from "vue-router";


const router=createRouter({
    history:createWebHistory(),
    routes:[
        {
            meta:{
                title:'wifi密码'
            },

            path:'/wifi',
            name:'wifi',
            component:import('../views/wifi.vue')
        }
    ]
})

export default router


router.beforeEach((to)=>{
    document.title=to.meta.title;
})

main.js

import { createApp } from 'vue'
import App from "./App.vue";
import router from "./router";

createApp(App).use(router).mount('#app')

App.vue

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

<script setup>

</script>


<style>

</style>

8.3 项目打包

不管我们使用的时vue-cli 还是vite进行项目构建和管理,将来要进项的项目的部署迁移到外网服务器时,都是需要使用打包后的项目代码,打包后的代码会进行压缩,并且只需要运行再http服务器下面即可,我们一般再公司里往往使用nginx打包

# vite 的打包
vite build
# vue-cli的打包命令
npm run build

可以用live-server测试服务器来运行打包后的项目

yarn add live-server
npm install -g live-server

import {createRouter,createWebHistory} from "vue-router";
  import wifi from '../views/wifi.vue'

  const router=createRouter({
    history:createWebHistory(),
    routes:[
      {
        meta:{
          title:'wifi密码'
        },

        path:'/',
        name:'wifi',
        component:wifi
      }
    ]
  })

  export default router


  router.beforeEach((to)=>{
    document.title=to.meta.title;
  })

直接输入 live-server 进入dist目录

posted @ 2022-04-05 19:38  始識  阅读(167)  评论(0)    收藏  举报