Vue学习笔记

一、三个简单操作

1.绑定文本

使用{{}}绑定文本

<div id="app">

        <!-- {{}}是表达式的意思 -->

        <h1>{{ message+"Vue" }}</h1>

    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <script>

        //创建vue的实例

        new Vue({

            //将div与Vue建立联系

            el:"#app",

            //选项里定义数据message的值为hello world

            data:{

                message:"hello world"

            }

        })

    </script>

结果为:hello worldVue

2.绑定属性

指令:

v-blind:属性

简写::属性

在页面插入图片:

 <body>

    <div id="app">

        <!-- 下面为简写,全部应该是这样:<img v-bind:src="pic" alt=""> -->

        <img :src="pic" alt="">

    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <script>

        //创建vue的实例

        new Vue({

            //将div与Vue建立联系

            el:"#app",

            pic:"images/img1.png"

            }

        })

    </script> 

3.绑定事件

事件修饰符:v-on

语法:v-on:事件类型=”方法”

简写:@事件类型=”方法”

点击按钮,弹出框

<div id="app">

        <button v-on:click="sayHi">按钮</button>

<button @click="sayHi">测试按钮</button>

    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <script>

        //创建vue的实例

        new Vue({

            methods:{

                sayHi(){

                    alert("hi")

                }

            }

        })

    </script>

eg1.点击按钮,切换图片

<div id="app">

        <!-- 下面为简写,全部应该是这样:<img v-bind:src="pic" alt=""> -->

        <img :src="pic" alt="">

        <button @click="changeImg">改变图片</button>

    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <script>

        //创建vue的实例

        new Vue({

            //将div与Vue建立联系

            el:"#app",

            methods:{

                changeImg(){

                    this.pic="images/img3.jpg";

                }

            }

        })

    </script>

eg2.点击加号数字加一,点击减号,数字减一

<body>

    <div id="app">

        <button @click="sub">-</button>

        <span>{{number}}</span>

        <button @click="add">+</button>

    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <script>

        new Vue({

            el:"#app",

            data:{

                number:0

            },

            methods:{

                add(){

                    this.number++

                },

                sub(){

                    if(this.number>0)

                       this.number--

                }

            }

        })

    </script>

</body>

二、使用vue/cli工具创建一个vue项目(需要Node环境)

1.步骤

全局安装vue/cli工具:cnpm install -g @vue/cli

使用vue/cli常见vue项目:vue create hello

启动服务器:npm run serve

2.组文件开发概述

以vue为后缀的文件是vue的单文件组件,可以理解为一个可以自定义的、有更强大功能的标签

用这样的组件拆分的方式开发项目,思路清晰,简介高效,而且还可以复用相同的组件

3.使用vue实践

在vscode里打开vue文件,打开终端,进入node_modules文件所在的目录,输入npm run serve

vue文件:

<template>

<!--网页模版,编写html文件-->

<!--该页面只能写一个标签,若要写多个标签,可以把标签写在另一个标签里面,如将h1标签写在div标签里-->

<div>

  <h1 :title="message">{{message}}</h1>

  <button @click="showHi">showHi</button>

</div>

</template>

<script>

// js代码

export default {

 data(){

  return {

    message:"hello vue"

  }

 },

methods:{

showHi(){

  alert("Hi~")

}

}

}

</script>

<style>

/* css代码 */

#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>

4.模版语法

(1)指令

是带有v-前缀的特殊属性

         1)绑定属性

v-bind

        2)绑定事件

v-on

        3)控制元素的可见性

v-if和v-show都可以控制元素的可见性,值为false不可见,为true可见

区别:v-if不渲染dom,而v-show渲染dom,再将元素设置成display:none

eg1.将两个元素隐藏:

<h1 v-if="false" :title="message">{{message}}</h1>

  <button v-show="flase" @click="showHi">showHi</button>

在调试工具里显示如下:

eg2.设置效果,若登录,显示“欢迎”,未登录显示“请登录”,不显示欢迎

  <template>

   <div>

  <p v-if="isLogin">欢迎</p>

  <p v-if="!isLogin"><a href="">请登录</a></p>

</div>

</template>

 <script>

 export default {

 data(){

    return {

      isLogin:false

    }

 }

}

</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>        

        4)显示列表

v-for

eg.制作页面显示如下效果

<template>

<div>

  <p v-if="isLogin">欢迎</p>

  <p v-if="!isLogin"><a href="">请登录</a></p>

  <ul>

    <li v-for="(fruit,index) of fruits" :key="index">

      <p>水果名称:{{fruit}}</p>

      <p>水果序号:{{index+1}}</p>

    </li>

  </ul>

  <table>

    <thead>

      <th>序号</th>

      <th>姓名</th>

      <th>年龄</th>

    </thead>

    <tbody>

      <tr v-for="(stu,i) of students" :key="i">

        <td>{{i+1}}</td>

        <td>{{stu.name}}</td>

        <td>{{stu.age}}</td>

      </tr>

    </tbody>

  </table>

</div>

</template>

<script>

export default {

 data(){

    return {

      isLogin:false,

      fruits:["香蕉","苹果","鸭梨"],

      students:[

        {name:"小明",age:1},

        {name:"花花",age:2},

        {name:"好好",age:3},

      ]

    }

 }

}

</script>

<style>

#app {

  font-family: Avenir, Helvetica, Arial, sans-serif;

  -webkit-font-smoothing: antialiased;

  -moz-osx-font-smoothing: grayscale;

}

</style>

效果如图:

(2)组件嵌套

(1)组件命名:大写字母开头,驼峰命名

(2)注册组件:

1)引入组件:

先在script标签里引入组件:import 组件名 from “路径”

再在export default里注册新组件,

eg.文件如下,现将组件HelloWorld.vue和MenuList.vue引入APP.vue中并使用

APP.vue文件内代码如下:

 <template>

<div>

<hello></hello>

<MenuList></MenuList>

</div>

</template>

<script>

import HelloWorld from"./components/HelloWorld.vue"

import MenuList from"./components/MenuList.vue"

export default {

  components:{

    hello:HelloWorld,

    MenuList:MenuList

    // 当把新建的组件与引入的组件命名为同名时,可以简写为(标签也同名):

    // HelloWorld,MenuList

  }

}

</script>

当组件作为标签在template里被使用时,在不与原来的组件重名的前提下,不区分大小写,同时还可以将两个单词用-分开,如

<menu-list></menu-list>一样可以使用

2)创建组件

在script标签里的export default里创建组件,然后在template里引用

eg.

  <template>

  <div>

 <h1>{{ message }}</h1>

</div>

</template>

 <script>

export default {

 data(){

        return {

        message:"hello vue!"

    }

 }

}

</script>      

(3)组件传值

1)组件关系

①父级向子级传递数据:使用属性传递数据

在父级中:

先引入子级,再在父级中设置要传递的属性值,再在child组件里添加属性,并将属性值传递,

在子级中,使用props引入属性,然后在template中绑定属性,即可实现父级向子级传递数据

父级:

<template>

  <div>

    <h1>hello</h1>

    <Child :msg="message"></Child>

  </div>

</template>

<script>

import Child from "./components/Child"

export default {

 components:{Child},

 data(){

  return {

    message:"父级向子级传递数据"

  }

 }

};

</script>

子级:

<template>

    <div>

        <h1>{{ msg }}</h1>

    </div>

</template>

<script>

export default {

 props:["msg"]

}

</script>

②子级向父级传递数据:使用自定义事件传值

父级:

先将子级的组件绑定事件(@事件名=”事件的函数”),再在export default里编辑事件函数

子级:

在子级里创建了一个按钮,绑定点击事件,再在export default里编辑点击事件的函数,其中使用$emit触发父级中的myevent事件,并传递数据

父级:

<template>

  <div>

    <h1>hello!{{childData}}</h1>

    <Child @myevent="changeData" :msg="message"></Child>

  </div>

</template>

<script>

import Child from "./components/Child"

export default {

 components:{Child},

 data(){

  return {

    message:"父级向子级传递数据",

    childData:""

  }

 },

 methods:{

  changeData(childData){

    this.childData=childData;

  }

 }

};

</script>


子级:

<template>

    <div>

        <h1>{{ msg }}</h1>

        <button @click="sendData">传递数据</button>

    </div>

</template>

<script>



export default {

 props:["msg"],

 data(){

    return{

        childData:"子级向父级传递数据"

    }

 },

 methods:{

    sendData(){

        // $emit可以触发事件

        this.$emit("myevent",this.childData)

    }

 }

}

</script>

eg.实现计数器效果

Carts.vue

<template>

    <div>

        <h1>购物车</h1>

        <ul>

            <li v-for="(v,i) of fruits" :key="i">

                {{ v.name }}

                单价:{{ v.price }}

                数量:

                <Counter 

                    :qu="v.qu"

                    :index="i"

                    @add="add"

                    @sub="sub">

                   

                </Counter>

            </li>

        </ul>

    </div>

</template>



<script>

import Counter from "./Counter.vue"

export default {

    components:{Counter},

  data(){

    return {

      fruits:[

        {name:"苹果",price:"3.14",qu:"0"},

        {name:"鸭梨",price:"2.17",qu:"0"},

        {name:"苹果",price:"2.17", qu:"0"},

      ]

    }

  },

  methods:{

    add(index){

        this.fruits[index].qu++

    },

    sub(index){

        if(this.fruits[index].qu>0){

            this.fruits[index].qu--

        }

       

    }

  }

};

</script>

Counter.vue

<template>

    <span>

        <button @click="sub">-</button>

        <span>{{ qu }}</span>

        <button @click="add">+</button>

    </span>

</template>

<script>

export default{

    props:["qu","index"],

    methods:{

        sub(){

            this.$emit("sub",this.index)

        },

        add(){

            this.$emit("add",this.index)

        }

    }

}

</script>

APP.vue

<template>

  <div>

    <Carts></Carts>

  </div>

</template>

<script>

import Carts from "./components/Carts"

export default {

  components:{Carts},

 

};

</script>

效果为:

③非父子级传递数据:定义一个共享数据的状态

创建一个store.js文件,在里面定义一个共享数据的状态,再在两个Brother.vue和Sister.vue组件里引入该文件,使两个文件通过js文件传递数据

先创建两个组件Brother.vue和Sister.vue,在两个组件里引入store.js文件,再在两个组件里使用store里的数据,然后,在Brother里创建按钮改变数据,点击按钮后,改变store里state的message,可以实现两个组件引用的message同步改变

Brother.vue

<template>

    <div>

        <h1>brother <button @click="changeData">改变数据</button></h1>

        <p>{{ state.message }}</p>

    </div>

</template>

<script>

import store from "../store"

export default{

    data(){

        return {

            // 使用store.js里的数据

            state:store.state

        }

    },

    methods:{

        changeData(){

            store.setStateMessage("bother")

        }

    }

}

</script>

Sister.vue

<template>

    <div>

        <h1>sister</h1>

        <p>{{ state.message}}</p>

    </div>

</template>

<script>

import store from "../store"

export default{

    data(){

        return {

            // 使用store.js里的数据

            state:store.state

        }

    }

}

</script>

store.js

export default{

    state:{

        message:"hello vue"

    },

    // 通过方法的方式操作state

    setStateMessage(str){

        this.state.message=str;

    }

}

APP.vue

<template>

  <div>   

    <brother></brother>

    <Sister></Sister>

  </div>

</template>

<script>

import Brother from "./components/Brother"

import Sister from "./components/Sister"

export default {

  components:{Brother,Sister}

 

};

</script>

总结:

将系统拆分成组件,一方面降低了功能的耦合,但是另一方面也提升了数据传输的难度,这里的利弊得失需要自己摸索

APP.vue

<template>

  <div>

    <Carts></Carts>

   

    <!-- <brother></brother>

    <Sister></Sister> -->

    <!-- <control :totalPrice="totalPrice"></control> -->

  </div>

</template>

<script>

import Carts from "./components/Carts"

// import Control from './components/Control.vue';

// import Control from "./components/Control.vue"

// import Brother from "./components/Brother"

// import Sister from "./components/Sister"

export default {

  components:{Carts},

};

</script>

Counter.vue

<template>

    <span>

        <button @click="sub">-</button>

        <span>{{ qu }}</span>

        <button @click="add">+</button>

    </span>

</template>

<script>

// import store from "../store"

export default{

    props:["qu","index"],

    methods:{

        sub(){

            this.$emit("sub",this.index)

        },

        add(){

            this.$emit("add",this.index)

        }

    }

}

</script>

Carts.vue

<template>

    <div>

        <h1>购物车</h1>

        <ul>

            <li v-for="(v,i) of fruits" :key="i">

                {{ v.name }}

                单价:{{ v.price }}

                数量:

                <Counter 

                    :qu="v.qu"

                    :index="i"

                    @add="add"

                    @sub="sub">

                </Counter>

            </li>

        </ul>

        <!-- <h1>总价为:</h1> -->

    </div>

</template>

<script>

import Counter from "./Counter.vue"

export default {

    components:{Counter},

  data(){

    return {

      fruits:[

        {name:"苹果",price:"3.14",qu:"0"},

        {name:"鸭梨",price:"2.17",qu:"0"},

        {name:"苹果",price:"2.17", qu:"0"},

      ]

    }

  },

  methods:{

    add(index){

        this.fruits[index].qu++

    },

    sub(index){

        if(this.fruits[index].qu>0){

            this.fruits[index].qu--

        }

       

    }

  }

};

</script>

三、计算属性与侦听器

computed:计算属性

watch:侦听器(监听器)

1.计算属性

data属性和computed属性定义的值都可以直接绑定在表达式中,如果某些值需要通过计算才能得到,那使用计算属性就再合适不过了

name=firstName+lastName

普通情况data与computed效果一样:

eg1.显示简单文字内容

<template>

  <div id="app">

    <h1>计算属性与侦听器</h1>

    <p>{{title}}</p>

    <p>{{ message }}</p>

  </div>

</template>

<script>

export default {

  data(){

    return{

      title:"hello world"

    }

  },

  // 下面的computed为对象,里面可以定义多个函数

  computed:{

    message(){

      return "hello computed"

    }

  }

}

</script>

如果显示内容为简单表达式,可以直接写在页面

eg2.简单表达式

<template>

  <div id="app">

    <h1>计算属性与侦听器</h1>

    <p>{{ firstName+lastName}}</p>

  </div>

</template>

<script>

export default {

  data(){

    return{

      firstName:"张",

      lastName:"三"

    }

  }

</script>

但是若表达式有多个计算过程,则使用computed属性

eg3.计数器效果

<template>

  <div id="app">

    <h1>计算属性与侦听器</h1>

    <p>单价:{{ price }}元</p>

    <p>数量:

      <button @click="sub">-</button>

      {{ quatity }}

      <button @click="add">+</button>

    </p>

    <p>折扣:{{ discount }}折</p>

    <p>总价:{{ totalPrice }}元</p>

  </div>

</template>

<script>

export default {

  data(){

    return{

      price:"99",

      quatity:1,

      discount:0.5

    }

  },

  computed:{

    totalPrice(){

      return this.price*this.quatity*this.discount

    }

  },

  methods:{

    sub(){

      if(this.quatity>0)

        this.quatity--

    },

    add(){

      this.quatity++

    }

  }

}

</script>

2.侦听器

侦听一个属性的变化

侦听器也可实现computed的效果:

eg.侦听器实现计数器效果

<template>

  <div id="app">

    <h1>计算属性与侦听器</h1>

    <p>单价:{{ price }}元</p>

    <p>数量:

      <button @click="sub">-</button>

      {{ quatity }}

      <button @click="add">+</button>

    </p>

    <p>折扣:{{ discount }}折</p>

    <p>总价:{{ totalPrice }}元</p>

  </div>

</template>

<script>

export default {

  data(){

    return{

      price:"99",

      quatity:0,

      discount:0.5,

      totalPrice:0

    }

  },

  // 监听quatity,每次quatity变化就会调用quatity方法

  watch:{

    quatity(val){

      this.totalPrice= this.price*val*this.discount;

    }

  },

  methods:{

    sub(){

      if(this.quatity>0)

        this.quatity--

    },

    add(){

      this.quatity++

    }

  }

}

</script>

总结:一个值的改变会影响多个值(或处理多件事),使用侦听器。(为了观察一个值)

多个值的改变,为了得到一个结果,使用计算属性(为了得到一个值),如上面的计数器,只要totalPrice

实际开发中,大部分问题都可以使用computed解决

四、生命周期钩子

1.作用

页面加载的时候,主动执行某些程序

2.Vue实例

(1)new Vue()创建一个Vue实例,所有组件其实都是Vue实例

(2)Vue实例的生命周期钩子(函数)

每个Vue实例在被创建时(new Vue)都要经过一系列的初始化过程,在该过程默认会调用一些Vue内置定义好的函数,这些函数就是Vue的生命周期钩子

created()组件初始化完成

mounted()模版已创建

eg.加载组件自动执行生命周期钩子

APP.vue文件

<template>

  <div id="app">

    <h1>hello world</h1>

  </div>

</template>

<script>

export default {

  mounted(){

    console.log("这是mounted函数的内容")

  },

  created(){

    console.log("这是created函数的内容")

  }

}

</script>

在该例中,并没有调用函数,但加载组件完成后会自动执行这两个函数

eg1.异步加载水果列表

<template>

  <div id="app">

    <h1>水果列表</h1>

    <p v-if="loading">loading...</p>

    <ul v-if="!loading">

      <li v-for="(item,index) of fruitList" :key="index">

      {{ item }}

      </li>

    </ul>

  </div>

</template>

<script>

export default {

  data(){

    return{

      fruitList:[],

      loading:true

    }

  },

  created(){

   this.getData();

  },

  methods:{

    getData(){

      // 通过计时器模拟一个ajax获取数据的方法

      setTimeout(()=>{

        this.fruitList=["香蕉","苹果","鸭梨"],

        this.loading=false

      },2000)

    }

  }

}

</script>

使用计数器设置2s的加载过程,组件加载过程显示loading...,加载完成后不显示loading...

五、插槽、DOM、过滤器

1.插槽

(1)作用:可以让我们更灵活地使用组件,增强组件的扩展性;

让我们创建更灵活、易扩展组件:自定义button,自定义table等;

开发或使用UI库,了解组件制作原理

eg.通过下面的按钮组件,如何实现按钮文本的自定义,可以通过属性传值,也可以通过插槽实现

eg.通过父级向子级传递数据(用属性),将按钮的名字自定义设置

APP.vue

<template>

  <div id="app">

    <MyButton text="提交"></MyButton>

    <MyButton text="注册"></MyButton>

  </div>

</template>

<script>

import MyButton from "./components/MyButton.vue"

export default {

  components:{MyButton}

};

</script>

MyButton.vue

<template>

    <div>

        <button>{{ text }}</button>

    </div>

</template>

<script>

export default{

    props:["text"]

}

</script>

eg.通过插槽实现自定义按钮名称

App.vue

<template>

  <div id="app">

    <MyButton >提交</MyButton>

    <MyButton >注册</MyButton>

  </div>

</template>

<script>

import MyButton from "./components/MyButton.vue"

export default {

  components:{MyButton}

};

</script>

MyButton.vue

<template>

    <div>

        <button>

            <!-- 插槽 -->

            <slot></slot>

        </button>

    </div>

</template>

自定义的内容会以插槽的方式插入到子组件的slot标签的位置

(2)具名插槽

可以在自定义的组件里写多个插槽,通过命名的方式区别不同的插槽显示不同的内容

使用template显示插入插槽的内容,使用v-slot区别不同插槽

eg.

Layout.vue

<template>

    <div>

        <div class="header">

            <slot name="header"></slot>

        </div>

        <div class="conent">

            <slot name="content"></slot>

        </div>

        <div class="footer">

            <slot name="footer"></slot>

        </div>

    </div>

</template>

APP.vue

<template>

  <div id="app">

    <Layout>

      <!-- template中的内容为要插入到插槽的内容 -->

      <!-- 使用v-slot对应插槽的名字 -->

      <template v-slot:header>

        <h1>header</h1>

      </template>

      <template v-slot:content>

        <h1>content</h1>

      </template>

      <template v-slot:footer>

        <h1>footer</h1>

      </template>

    </Layout>

  </div>

</template>

<script>

import Layout from "./components/Layout.vue"

export default {

  components:{Layout}

};

</script>

2.DOM操作

(1)之前学习回顾

DOM:文档对象模型

DOM节点:元素节点、属性节点、文本节点

通过操作DOM实现页面效果:jQuery

eg1.将h1标签 的内容修改成hello world

$(“h1”).text(“hello world”)

(2)虚拟DOM

Vue中的数据变化并不是直接改变DOM,而是通过改变虚拟DOM,将真实的DOM与虚拟的DOM作比较,并计算变更差异,进而修改DOM中有变化的内容

获取真实DOM:

1)获取元素样式

①可以直接通过js方法获取

先通过querySelector获取节点,再用window.getComputedStyle()获取节点的样式

<template>

  <div id="app">

    <div id="box">hello</div>

  </div>

</template>

<script>

// import Layout from "./components/Layout.vue"

export default {

  mounted(){

    let box=document.querySelector("#box");

    let style=window.getComputedStyle(box);

    console.log(style.height)

  }

};

</script>

<style>

div{

  width:100px;

  height: 100px;

  background-color: red;

}

</style>

②通过vue获取dom(更方便)

在标签里指定属性ref,再使用vue实现获取真实dom

<template>

  <div id="app">

    <div ref="box">hello</div>

  </div>

</template>

<script>

export default {

  mounted(){

    let box=this.$refs.box;

    let style=window.getComputedStyle(box);

    console.log(style.height)

  }

};

</script>

<style>

div{

  width:100px;

  height: 100px;

  background-color: red;

}

</style>

2)获取元素宽度

<template>

  <div id="app">

    <h1 ref="title">hello world</h1>

  </div>

</template>

<script>

export default {

  mounted(){

    let h1=this.$refs.title;

    let width=window.getComputedStyle(h1).width;

    console.log(width)

  }

};

</script>

<style>

div{

  width:200px;

  height: 100px;

  background-color: red;

}

</style>

总结:

Vue应用开发的过程中,大部分情况是不需要获取真实DOM的,不要把jQuery的思想带到Vue中

3.过滤器

通过固定算法重新组织数据

将字符串拆分成字母:使用split,再将字母连接用join

APP.vue文件如下:

<template>

  <div id="app">

    <h1>{{ message }}</h1>

  </div>

</template>

<script>

export default {

  data(){

    return{

      message:"hello"

    }

  }

};

</script>

在调试工具的控制台分别输入如下语句:

let str="hello";

str.split("").join();

结果为:'h,e,l,l,o'

接下来通过过滤器实现该功能

<template>

  <div id="app">

    <!-- |前面是原始数据,后面是过滤器 -->

     <h1>{{ message | mySplit}}</h1>

     <h1>{{ title | mySplit}}</h1>

  </div>

</template>

<script>

export default {

  // 过滤器

  filters:{

    // 定义多个过滤器

    // 有形参,为过滤器输入的值

    mySplit(value){

      // spilt()将字符串拆分成数组,join()将数组连接成字符串

      return value.split("").join();

    }

  },

  data(){

    return{

      message:"hello",

      title:"world"

    }

  }

};

</script>

eg1.日期格式化

将“2020-1-1”转化成“2020年1月1日”

<template>

  <div id="app">

    <h1>{{ date1 |dateFormate }}</h1>

    <h1>{{ date2 |dateFormate }}</h1>

  </div>

</template>

<script>

export default {

  filters:{

    dateFormate(value){

      let date=new Date(value);

      let year=date.getFullYear();

      let month=date.getMonth()+1;

      let d=date.getDate();

      return `${year}年${month}月${d}日`

    }

  },

  data(){

    return{

      date1:"2020-1-1",

      date2:"2022-6-5"

    }

  }

};

</script>

六、表单

1.数据的双向绑定

通过v-model指令在文本输入框中创建双向数据绑定

eg1.输入文本,将文本内容传给h1

<template>

  <div id="app">

    <h1>{{message}}</h1>

    <input type="text" v-model="message">

  </div>

</template>

<script>

export default {

  data(){

    return{

      message:""

    }

  }

};

</script>

eg2.简单提交表单

<template>

  <div id="app">

    <!-- 表单提交有一个刷新页面的默认行为,取消该行为:用prevent修饰符 -->

    <form @submit.prevent="postData">

      <!-- ajax实现表单提交 -->

      <input type="text" v-model="message">

      <button>提交表单</button>

    </form>

  </div>

</template>

<script>

export default {

  data(){

    return{

      message:""

    }

  },

  methods:{

    postData(){

      console.log(this.message)

    }

  }

};

</script>

2.常用表单控件

文本输入框、密码输入框、下拉菜单、单选框、复选框、提交按钮

eg实现提交表单

<template>

  <div id="app">

    <!-- 表单提交有一个刷新页面的默认行为,取消该行为:用prevent修饰符 -->

    <form @submit.prevent="postData">

      <!-- ajax实现表单提交 -->

      <div>

         <label for="">用户名:</label>

         <input type="text" v-model="formData.username">

      </div>

     <div>

      <label for="">密码:</label>

      <input type="password" v-model="formData.password">

     </div>

     <div>

      <label for="">爱好:</label>

      <select v-model="formData.hobby">

        <!-- 此处绑定是value值 -->

        <option value="basketball">篮球</option>

        <option value="football">足球</option>

        <option value="羽毛球">羽毛球</option>

      </select>

     </div>

     <div>

      <label for="">性别:</label>

      <label for="">男</label>

      <!-- 男女两个v-model要绑定一个属性值才能实现单选 -->

      <input type="radio" value="男" v-model="formData.sex">

      <label for="">女</label>

      <input type="radio" value="女" v-model="formData.sex">

     </div>

     <div>

      <label for="">技能:</label>

      <label for="">前端</label>

      <input type="checkbox" value="前端" v-model="formData.skill">

      <label for="">java</label>

      <input type="checkbox" value="java" v-model="formData.skill">

     </div>

      <button>提交表单</button>

    </form>

  </div>

</template>

<script>

export default {

  data(){

    return{

      // 将表单数据写在一个对象里

      formData:{

        username:"",

        password:"",

        hobby:"",

        sex:"",

        skill:[]

      }

    }

  },

  methods:{

    postData(){

      console.log(this.formData)

    }

  }

};

</script>

效果如下:

七、数据交互

1.知识回顾

http协议:前端(浏览器)发送请求,服务器给予响应

请求方法:get(查询)、post(添加)、put(修改)、delete(删除)

ajax:不刷新页面与后台交互数据

axios:封装好的ajax模块

koa:基于node的web框架

2.示例

初始化vue项目

①创建vue项目

②npm install --save axios

③import axios from “axios”

实现水果列表的添加和删除

APP.vue

<template>

    <div>

        <form @submit.prevent="postData">

            <input type="text" v-model="fruit">

            <button>添加</button>

        </form>

        <ul>

            <li v-for="item,index of fruitList" :key="index">

                {{item}}

                <!-- 根据索引删除 -->

                <button @click="del(index)">删除</button>

            </li>

        </ul>

    </div>

</template>

<script>

import axios from "axios";

export default {

    data(){

        return{

            fruit:"",

            fruitList:[]

        }

    },

    methods:{

        //获取数据

        getFruitList(){

            axios.get("http://127.0.0.1:3000/fruits").then(res=>{

                this.fruitList=res.data;

            })

        } ,

        //添加数据

        postData(){

            axios.post("http://127.0.0.1:3000/fruits",{

               fruit:this.fruit 

            }).then(res=>{

                //响应后重新获取数据,更新列表

                this.getFruitList();

            })

        },

        // 删除数据

        del(index){

            axios.delete(`http://127.0.0.1:3000/fruits${index}`)

            .then(res => {

                //响应后重新获取数据,更新列表

                this.getFruitList();

            })

        }

    },

    created(){

            this.getFruitList();//初始化数据

        }

}

</script>

server.js

const Koa=require("koa");

const router=require("koa-router")();//设置路由

const parser=require("koa-parser");//获取post请求数据

// 安装cnpm install --save koa2-cors

const cors=require('koa2-cors');//允许跨域

const app=new Koa();

app.use(parser());

app.use(cors());

//模拟数据库

const fruitList=["香蕉","苹果","鸭梨"];

//get方法:获取水果列表

router.get("/fruits",async ctx => {

    ctx.body=fruitList;

})

//post添加

//注意:使用post要添加koa-parser(在上面)

router.post("/fruits",async ctx => {

    //获取数据

    let fruit=ctx.request.body.fruit;

    //push方法可以在array结尾追加数据

    fruitList.push(fruit);

    ctx.body=true;

})

//put修改s

router.put("/fruits/:index",async ctx => {

    let index=ctx.params.index;

    let fruit=ctx.request.body.fruit;

    dataList.splice(index,1,fruit);

    ctx.body=fruitList;

})

//delete删除

router.delete("/fruits/:index", async ctx => {

    let index=ctx.params.index;

    fruitList.splice(index,1);

    ctx.body=true;

})

app.use(router.routes());

app.listen("3000",()=>{

    console.log("server is running");

})

3.项目实践

在真实的项目中,我们会对axios进行进一步封装,以便于更轻松的发送请求

通过配置文件,设置项目开发与项目部署的无缝衔接

八、路由

之前讲解的所有功能都是在同一个页面完成的,真正的web应用一定是有多个页面的,这就需要路由

根据创建的Vue项目的结构可知,可以通过router-link标签,再用to实现跳转(自定义组件的页面),而router文件下的index.html文件中,引入了组件及路径

注意:router-link标签一定要使用to,否则不显示内容

一般页面中的局部内容的组件放在components里用来复用,整体页面的组件放在views里,基本不是复用,而是一个单独的页面

eg.制作博客系统,实现菜单跳转和登录、注销功能,未登录不可访问任何页面

文件如下:

APP.vue

<template>

  <div id="app">

    <nav>

      <!-- router-link类似于a标签,可实现页面跳转,跳转属性是to -->

      <!-- a标签跳转的属性是href -->

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

    <router-link to="/blog">博客</router-link>|

    <router-link to="/video">视频</router-link>

    <span v-if="showUser"> | |欢迎:{{ username }} <button @click="logout">注销</button></span>

  </nav>

  <!-- router-view是一个显示网页内容的位置 -->

  <router-view/>

  </div>

</template>

<script>

export default{

  data(){

    return {

      username:localStorage.getItem("usr"),

      showUser:localStorage.getItem("usr")

    }

  },

  watch:{

    //当路由路径改变就执行函数(即切换页面就执行

   '$route.path':function(){

      this.username=localStorage.getItem("usr"),

      this.showUser=localStorage.getItem("usr")

    }

  },

  methods:{

    logout(){

      //清空本地存储的值

      localStorage.clear();

      this.$router.push("/login")

    }

  }

}

</script>

index.js

import { createRouter, createWebHistory } from 'vue-router'

import HomeView from '../views/HomeView.vue'

import BlogView from "../views/BlogView.vue"

import VideoView from "../views/VideoView.vue"

import LoginView from "../views/LoginView.vue"

const routes = [

  {

    path: '/',

    name: 'HomeView',

    component: HomeView

  },

  {

    path:"/blog",

    name:"BlogView",

    component:BlogView

  },

  {

    path:"/video",

    name:"VideoView",

    component:VideoView

  },

  {

    path:"/login",

    name:"LoginView",

    component:LoginView

  }

]

const router = createRouter({

  history: createWebHistory(process.env.BASE_URL),

  routes

})

//导航守卫:router有一个方法beforeEach函数,其参数为一个回调函数,

//回调函数有三个参数,分别是to,from,next

//to:访问到哪,from:从哪访问,next:继续访问

router.beforeEach((to,from,next)=>{

  //如果to的路径不是登录页面的路径

  if(to.path!=="/login"){

    //如果可以拿到本地存储用户名(说明已经登录),则继续访问

    if(localStorage.getItem("usr")){

      next();

      //如果没有本地存储用户名(即已经注销或还未登录),则返回登录页

    }else{

      //重定向

      next("/login")

    }

  }

  //如果to的路径是登录页面的路径,说明登录页可以正常访问,即已经登录过了,那么就正常访问

  else{

    next();

  }

})

export default router

BlogView.vue

<template>

    <div>

        <ul>

            <li>

                <router-link to="">JavaScript教程</router-link>

            </li>

            <li>

                <router-link to="">JavaScript教程</router-link>

            </li>

            <li>

                <router-link to="">JavaScript教程</router-link>

            </li>

        </ul>

    </div>

</template>

LoginView.vue

<template>

    <div>

        <form @submit.prevent="doLogin">

            <input type="text" v-model="username">

            <input type="text">

            <button>登录</button>

        </form>

    </div>

</template>

<script>

export default{

    data(){

        return {

            username:""

        }

    },

    methods:{

        doLogin(){

            console.log(this.username);

            //本地存储:localStorage对象有一个方法setItem,可以将username的值赋给变量usr(自定义名称),从而达到将username的值存起来的效果

            //然后可以在其他页面通过localStorage.getIem("usr")拿到username的值

            localStorage.setItem("usr",this.username);

            //Vue的路由提供一个编程式导航功能:先获取router对象,再用push("路径")设置跳转页面的路径

            //跳转到首页

            this.$router.push("/")

        }

    }

}

</script>

HomeView.vue

<template>

  <div class="home">

    <h1>首页</h1>

  </div>

</template>

<script>

export default {

}

</script>

VideoView.vue

<template>

    <div>

        <h1>视频</h1>

        <video src="" controls></video>

    </div>

</template>

九、ElementUI

是一个基于Vue的UI框架,还有其他的基于Vue的UI框架,如:vux,vant

可查看网站https://element.eleme.cn/#/zh-CN获取详细资料

1.引入ElementUI

安装:npm install --save element-ui

在main.js文件中:

引入样式:import ElementUI from 'element-ui';

import 'element-ui/lib/theme-chalk/index.css';

加载插件:Vue.use(ElementUI);

2.组件

按钮、表单、表格

eg.完成一个学生信息管理功能,包括学员信息的查询、添加、删除功能,要求如下:

后台使用koa实现数据接口

添加功能使用对话框弹出表单(Dialog)

删除需要有确认提示(MessageBox)

APP.vue

<template>

  <div>

    <!-- dialogVisible是控制对话框是否隐藏的变量 -->

    <el-button type="text" @click="dialogVisible = true">添加学生</el-button>

    <!-- data绑定了一个数组 -->

    <!-- 表格 -->

    <el-table :data="studentList" border style="width: 100%">

      <el-table-column prop="id" label="ID" width="120">

      </el-table-column>

      <el-table-column prop="name" label="姓名" width="120">

      </el-table-column>

      <el-table-column prop="age" label="年龄" width="120">

      </el-table-column>

      <el-table-column label="操作" width="100">

        <template slot-scope="scope">

          <el-button @click="del(scope.row.id)" type="text" size="small">删除</el-button>

        </template>

      </el-table-column>

    </el-table>

    <!-- 对话框 -->

    <el-dialog title="添加学生信息" :visible.sync="dialogVisible" width="30%" >

      <el-form>

        <el-form-item label="ID">

          <el-input v-model="form.id"></el-input>

        </el-form-item>

        <el-form-item label="姓名">

          <el-input v-model="form.name"></el-input>

        </el-form-item>

        <el-form-item label="年龄">

          <el-input v-model="form.age"></el-input>

        </el-form-item>

      </el-form>

      <span slot="footer" class="dialog-footer">

        <el-button @click="dialogVisible = false">取 消</el-button>

        <el-button type="primary" @click="onSubmit">确 定</el-button>

      </span>

    </el-dialog>

  </div>

</template>

<script>

import axios from "axios"

export default {

  methods: {

    onSubmit(){

      axios.post("http://127.0.0.1:3000/student",{

        student:this.form

      }).then(()=>{

        //重新获取列表

        this.getStudentList();

        // 关闭对话框

        this.dialogVisible=false;

      })

    },

    del(id) {

      this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {

        confirmButtonText: '确定',

        cancelButtonText: '取消',

        type: 'warning'

      }).then(() => {

        axios.delete(`http://127.0.0.1:3000/student/${id}`).then(() => {

          this.$message({

            type: 'success',

            message: '删除成功!'

          });

          this.getStudentList();

        });

      }).catch(() => {

        this.$message({

          type: 'info',

          message: '已取消删除'

        });

      });

    },

    getStudentList() {

      axios.get("http://127.0.0.1:3000/student").then((res) => {

        this.studentList = res.data;

      })

    }

  },

  data() {

    return {

      studentList: [],

      //设置对话框可见

      dialogVisible:false,

      // 表单

      form:{

        id:"",

        name:"",

        age:""

      }

    }

  },

  created() {

    this.getStudentList();

  }

}

</script>

server-ele.js

const Koa=require("koa");

const router=require("koa-router")();//设置路由

const parser=require("koa-parser");//获取post请求数据

// 安装cnpm install --save koa2-cors

const cors=require('koa2-cors');//允许跨域

const static=require("koa-static");

const app=new Koa();

app.use(cors());

app.use(parser());

app.use(static(__dirname+"public"));

app.use(router.routes());

//模拟数据库

const studentList=[

    {id:1,name:"小明","age":2},

    {id:2,name:"小红","age":4},

    {id:3,name:"小亮","age":6},

];

router.get("/student",async ctx=>{

    ctx.body=studentList;

})

router.post("/student",async ctx=>{

    let student=ctx.request.body.student;

    studentList.push(student);

    ctx.body=true;

})

router.delete("/student/:id",async ctx=>{

    let id=ctx.params.id;

    //map遍历,通过id实现删除功能

    studentList.map((item,index)=>{

        if(item.id==id){

            studentList.splice(index,1);

        }

    })

    ctx.body=true;

})

app.listen("3000",()=>{

   console.log("server is running");

})

十、项目部署

1.基于Vue的web项目

进入Vue文件夹,通过命令npm run build将VUe项目打包成一个静态文件,会在该文件夹里生成一个dist文件夹,将文件夹里的文件全部拷贝到public即可通过服务器端口访问vue设置的页面然后在服务器中发布

将静态html通过koa发布到服务器上

当项目的开发阶段和发布阶段的阈值不同时,可以通过配置文件实现阈值的切换

配置文件:

.env.development开发阶段配置文件

.env.production生产环境的配置文件,即发布后的

注意:命名变量必须以VUE_APP_XXXX命名

eg.

在.env.production文件:

 VUE_APP_TITLE="生产环境" 

在.env.development文件:

 VUE_APP_TITLE="开发环境"

 在APP.vue文件中引入该内容:
<template>

   <div id="app">

    <h1>{{ title }}</h1>

  </div>

</template>

 <script>

export default{

  data(){

    return{

      title:process.env.VUE_APP_TITLE

    }

  }

}

</script>    

再输入命令npm run build生成dist文件,将该文件的内容拷贝到public文件内,然后运行js文件、vue文件

效果如下:

在8080端口(开发阶段):

而在服务器设置的端口(发布阶段)

2.实践

在vue文件夹里创建两个配置文件,分别为:

.env.development

.env.production

主要文件结构如下:

·project

   ·vue

        ·dist

         ·src

                ·APP.vue

       ·.env.development

       ·.env.production

 ·server.js

  ·public

在.env.development文件中写入:VUE_APP_BASE_URL="http://127.0.0.1:3000"

在.env.production文件中写入:

VUE_APP_BASE_URL=""

将原来APP.vue文件中,路径改为:

然后在vue文件目录下,输入命令npm run build产生dist文件,再将该文件夹下的文件全部拷贝至public文件下,然后分别运行js文件和vue文件,最后即可在服务器里设置的端口(127.0.0.1:3000)打开vun创建的页面,并实现相关功能

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值