Vue2

Vue2

简介

Vue是一套用于构建用户界面的渐进式框架,与其它大型框架不同的是,Vue被设计为可以自底层向上逐层应用

Vue的核心库只关注视图,不仅易于上手,还便于与第三方库或既有项目整合

  • VUE是一款由数据驱动视图的前端框架,它基于MVVM(Model View View Model)模式,实现数据与视图的双向绑定

  • VUE是渐进式框架,VUE提供了一个核心库,用于渲染视图,它占空间小,能完成VUE中的常规基础操作

环境准备

启动powershell,安装vue脚手架

npm install -g @vue/cli

创建一个空文件夹作为项目工程目录

当前目录下启动cmd,打开vue工程的图形管理界面

vue ui

如果未打开浏览器,访问:http://localhost:8000/project/select 查看图形界面

创建项目

包管理器选用npm,选择手动配置项目,添加功能RouterVuex

vue版本改成2.x,Lint错误集点选择第一项即可

使用此向导创建的vue工程自带git功能

安装vue开发者工具devtools

开发工具下载:https://devtools.vuejs.org/guide/installation.html

在项目目录下启动cmd,输入npm run serve,启动前端服务器

官网配置信息参考:https://webpack.js.org/configuration/dev-server/

修改服务端口

在vue.config.js文件下添加配置

/*引入vue配置*/
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    port: 7070
  }
})

跨域访问

在前端服务器中添加代理

防止跨域问题

devServer: {
    proxy: {
        '/api': {
          //目标服务器
          target: 'http://localhost:8080',
          changeOrigin: true,
          /*去除路径中的 /api */
          pathRewrite: { '^/api': '' },
        },
    }
}

代理配置

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    proxy: {
      '/api': {
        //目标服务器
        target: 'http://localhost:8080',
        changeOrigin: true,
        /*去除路径中的 /api */
        pathRewrite: { '^/api': '' },
      },
    },
    port: 7070
  }
})

前端测试通过代理访问后端服务器:http://localhost:7070/api/demo

此时真实访问地址:localhost:8080/demo

原理:

浏览器端不允许跨域,但是服务器允许,在本地npm run serve等命令时实际上用了node运行了一个前端服务器,我们的请求先由浏览器发送给了前端服务器,然后通过前端服务器根据我们自己的proxy配置来将请求发送给后端服务器

Vue项目结构

基本模块有:

  • assets 静态资源
  • components 可重用组件
  • router 路由
  • store 数据共享
  • views 视图组件

可能还会有的模块:

  • api 负责和后端api交互,发送fetch,xhr请求,接收响应

  • plugins 插件

Vue组件

vue组件一般都以.vue结尾

每个组件一般由三个部分组成

<template></template>

<script></script>

<style></style>
  • template 模板部分,由它生成html代码
  • script 代码部分,控制模块的数据来源和行为
  • style 样式部分

App.vue

vue的最顶层组件

app.vue是主组件,是页面入口文件,是vue页面资源的首加载项

所有的页面都是在app.vue中进行切换的

可以理解为所有的路由都是app.vue的子组件

app.vue中不但可以当做是网站首页,也可以写所有页面中公共需要的动画或者样式,不在上面写代码也可以

我们也可以改写此组件,可以发现实时更新到了浏览器中

<template>
  <h1>Hello,world</h1>
</template>

<script></script>

我们可以通过JavaScript动态获取后端数据来渲染到模板上

Vue约定——options

在Vue框架中,所有的JavaScript代码都需要导出一个options对象

options对象内部可以带有data属性,用于返回数据

  • {{msg}} 两个大括号嵌套用于获取data属性中的数据
<template>
  <h1>{{msg}}</h1>
</template>

<script>
const options = {
  data: function(){
    return {msg:"Hello Vue!"};
  }
};

export default options;

</script>

通过代码我们可以发现,vue通过main.js来调用app.vue这个组件

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

通过vue提供的h函数来解析app.vue里面的内容,形成虚拟节点

然后通过$mount函数整合到html中,这里通过id选择器绑定到原始页面index.html中去

我们如果想替换原始App.vue组件,只需要修改import语句即可

例如:import App from './views/ExampleView1.vue'

Vue绑定

一般来说标签内容用 {{}}绑定

标签属性内容用:绑定

标签属性事件用@绑定

表单属性一般才会使用双向绑定

绑定文本

template内只能有一个顶层根元素

<!--这种写法是错误的-->
<template>
    <h1>{{name}}{{age}}</h1>
</template>
<!--这种写法是正确的-->
<template>
    <div>
        <h1>{{name}}</h1>
        <h1>{{age}}</h1>
    </div>
</template>

<script>
const options = {
  data: function(){
    return {name:"张三",age:20};
  }
};

export default options;

</script>

==data属性必须是一个函数,函数返回的对象就是需要返回的数据==

★vue标签属性绑定

使用v-bind绑定标签属性

<template>
  <div>
    
    <div><input type="text" v-bind:value="msg"/></div>
    
    <div><input type="date" v-bind:value="date"/></div>

  </div>
</template>

<script>
const options = {
  data: function(){
    return {
            msg:"Hello Vue!",
            date:"2022-10-1"
  };
  }
};

export default options;

</script>

由于v-bind经常被使用到因此也可以省略只保留 :value=”xxx”

<div><input type="text" :value="age"/></div>

★vue事件绑定

提供一个methods属性,里面可以存放一些方法

<template>
  <div>
    <div>
      <input type="button" value="点我" v-on:click="m1"/>
    </div>
  </div>
</template>

<script>
const options = {
  data: function(){
    return {};
  },
  methods : {
    m1: function(){
      alert("m1");
    },
    /* 可以省略function简写 */
    m2() {
      alert("m2");
    }
  }
};

export default options;

</script>

v-on也有简写模式 例如可以改成 @click=”m1”

this

数字加减操作

在函数中的this代表返回的data属性

如果要在methods中操作data中某个属性的值就需要用this来引用这个属性

<template>
  <div>
    <div>
      <input type="button" value="加1" @click="m1"/>
    </div>
    <div>
      <input type="button" value="减1" @click="m2"/>
    </div>
    <div>{{count}}</div>
  </div>
</template>

<script>
const options = {
  data: function(){
    return {count: 0};
  },
  methods : {
    m1(){
      this.count ++;
    },
    m2() {
      this.count --;
    }
  }
};

export default options;

</script>

★双向绑定

原来我们只是将js中的数据绑定到了标签属性上,但是用户输入的属性并不会绑定到js的属性上,因此我们利用双向绑定来实现用户输入数据绑定到js

<template>
    <div>
      <div>
         <label for="">请输入姓名</label>
         <input type="text" v-model="name"/>
      </div>
      <div>
        <label for="">请输入年龄</label>
        <input type="text" v-model="age"/>
      </div>
  </div>
</template>

<script>
const options = {
  data: function(){
    return {
      name:"张三",
      age:20
    };
  },
  methods : {}
};

export default options;

</script>

在浏览器中修改数据,发现调试工具中js中的数据也改变了

利用vue调试工具来查看js中数据的变更

单选框

<template>
    <div>
      <div>
        <label for="">请选择性别</label>
        男 <input type="radio" value="男" v-model="sex"/>
        女 <input type="radio" value="女" v-model="sex"/>
      </div>
  </div>
</template>

<script>
const options = {
  data: function(){
    return {
      sex:'男'
    };
  },
  methods : {}
};

export default options;

</script>

多选框

此时js中的数据用数组存储

<template>
    <div>
      <div>
        <label for="">请选择爱好</label>
        游泳 <input type="checkbox" value="游泳" v-model="hobby"/>
        跳舞 <input type="checkbox" value="跳舞" v-model="hobby"/>
        打篮球 <input type="checkbox" value="打篮球" v-model="hobby"/>
      </div>
  </div>
</template>

<script>
const options = {
  data: function(){
    return {
      hobby: []
    };
  },
  methods : {}
};

export default options;

</script>

下拉框

<template>
    <div>
      <div>
        <label for="">请选择你的专业</label>
        <select v-model="subject">
          <option value="计科">计科</option>
          <option value="软件">软件</option>
          <option value="物联网">物联网</option>
          <option value="信息安全">信息安全</option>
        </select>
      </div>
  </div>
</template>

<script>
const options = {
  data: function(){
    return {
      subject: []
    };
  },
  methods : {}
};

export default options;

</script>

计算属性

文本拼值

可以使用 + 将两个字符串拼接,当然我们也可以直接调用方法

<template>
  <div>
      <h2>{{fullName()}}</h2>
  </div>
</template>

<script>
const options = {
  data: function(){
    return {
      firstName:"三",
      lastName:"张"
    };
  },
  methods : {
    fullName(){
      return this.lastName + this.firstName;
    }
  }
};

export default options;

</script>

这种方法的弊端是每次取值都要调用方法

这种方法不是最好的,我们可以使用计算属性来取值

  • 写法和methods类似,当然这种方法就不用加括号了
  • 计算属性是具有缓存的,如果第一次使用了此属性,那么之后再用到便不会重复进入方法加载属性
<template>
  <div>
      <h2>{{fullName}}</h2>
  </div>
</template>

<script>

const options = {
  data: function(){
    return {
      firstName:"三",
      lastName:"张"
    };
  },
  methods : {
  },
  computed: {
    fullName() {
      return this.lastName + this.firstName;
  }
}
};

export default options;

</script>

v-for遍历集合

参考博客

使用v-for属性来遍历集合,需要指定遍历

语法:(元素,索引) in 数组

需要指定key,一般绑定index

<div v-for="(x,index) in res.data" :key="index">
  {{ x }}
  <br>
</div>

接收后端传值并遍历数组

<template>
  <div>
      <div>
        <input type="button" value="获取远程数据" @click="getRequest">
      </div>
      <div>
        响应的数据:
        <!--必须指定key绑定为index,即遍历的索引-->
        <div v-for="(x,index) in res.data" :key="index">
          <!--取到遍历的每个属性-->
          {{x}}
          <br>
        </div>
      </div>
</template>

<script>
import axios from 'axios'

const options = {

  data: function (){
     return {
       res : ""
     }
  },

  methods : {
    async getRequest(){

      let name = encodeURIComponent("os467");
      let password = "os4670";

      //发送请求头的数据
      axios.post('/api/demo',{},
          {
            params:{
              name: name,
              password: password
            }
          }
      ).then(res => {
        this.res = res;
      });

    }
  }
};

export default options;

</script>

v-if v-else条件渲染

如果没有数据的时候显示暂无数据的字段

<div>
    响应的数据:
    <div v-if="res.length > 0">
        <div v-for="(x,index) in res" :key="index">
            {{ x }}
            <br>
        </div>
    </div>
    <div v-else>暂无学生数据</div>
</div>
  • v-else必须和v-if连接使用

组件加载完毕函数

在页面渲染完毕后执行的函数,如果需要用到options中的函数需要用到this

const options = {
    mounted() {
        this.getRequest();
    }
}

组件重用

子组件

设计按钮子组件

<template>
  <div class="button">
      <button class="up" @click="buttonDown">
       按钮
        </button>
   </div>
</template>
<script>
const options = {
  methods:{
    buttonDown(){
      alert("触发了按钮");
    }
  }
};
export default options;
</script>

<style>
button,.up {
  color: #42b983;
  border-top-color: #cccccc;
  border-left-color: #cccccc;
}
</style>

子组件插槽

<slot></slot>标签,在父组件使用子组件的时候可以在子组件的标签内部添加内容,slot仅起到占位作用,会被添加的内容替换掉

<template>
   <div class="button">
      <button class="up" @click="buttonDown">
            <slot></slot>
        </button>
   </div>
</template>

父组件

在页面中使用子组件的标签

<template>
    <div>
    重用组件:
    <my-components></my-components>
    </div>
</template>
<script>
    import MyComponents from "@/components/MyComponents";
    const options = {
        components:{
            MyComponents
        }   
    };
    export default options;
</script>

vue组件自定义属性

在子组件的options中添加props属性

<template>
  <div class="button" :class="[type,size]"><button class="up" @click="buttonDown">
    按钮
  </button></div>
</template>
<script>
const options = {
  props:["type","size"],
  methods:{
    buttonDown(){
      alert("触发了按钮");
    }
  }
};
export default options;
</script>

这样就可以在父模块使用该组件的时候添加标签,多个属性用class="[field1,field2]"表示

★路由Router

vue属于单页面应用,所谓的路由,就是根据浏览器路径不同,用不同的视图组件替换这个页面内容展示

配置路由

path: 路由的路径

component: 用于替换的组件

这里我们使用静态import导入的方式导入vue组件

import Vue from 'vue'
import VueRouter from 'vue-router'
import TableComponent from "@/components/TableComponent";

Vue.use(VueRouter)

const routes = [
  {
    path:"/",
    component:TableComponent
  }
]

const router = new VueRouter({
  routes
})

export default router

main.js改为导入我们的路由文件,默认访问index.js

import router from './router/myRouter'

在主页面中引入路由组件

<router-view></router-view> : 需要被替换的路由组件

<template>
  <div>
    <el-container>
      <el-aside width="200px">
        <cascader-component></cascader-component>
      </el-aside>
      <el-container>
        <el-header>标题</el-header>
        <el-main>
          <div>
            <router-view></router-view>
          </div>
        </el-main>
        <el-footer>页尾</el-footer>
      </el-container>
    </el-container>


  </div>
</template>

动态导入组件

当打包构建应用时,JS包会变得非常大,影响页面加载

如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效

结合Vue的异步组件和Webpack的代码分割功能,轻松实现路由组件的懒加载(按需加载

  • 只需要将component用一个动态import函数导入即可
    • 此时只有当用到此组件时才会加载js代码,提高了主页面的加载速度
const routes = [
  {
    path:"/",
    component:() => import('@/components/TableComponent.vue')
  }
]

嵌套路由

定义子路由

子路由路径和父路由的路径合在一起才是最终访问子路由的路径

const routes = [
  {
    path:"/",
    component:() => import('@/components/TableComponent.vue'),
    children:[
      {
        path:'/text',
        component:() => import('@/components/tableComponentChildren/TextComponent.vue')
      },
      {
        path: '/button',
        component:() => import('@/components/tableComponentChildren/ToTextComponent.vue')
      }
    ]
  }
]

父路由组件中加入<router-view></router-view> 标签,用来显示子路由组件

通过重定向加载子组件

redirect:加载子组件的地址

const routes = [
  {
    path:"/",
    component:() => import('@/components/TableComponent.vue'),
    redirect:'/button',
    children:[
      {
        path:'/text',
        component:() => import('@/components/tableComponentChildren/TextComponent.vue')
      },
      {
        path: '/button',
        component:() => import('@/components/tableComponentChildren/ToTextComponent.vue')
      }
    ]
  }
]

匹配其它路由

例如输入未知的路由,我们可以设置一个通配的404NotFound组件路由

***** 号匹配所有无路由规则的路径

const routes = [
  {
    path:"/",
    component:() => import('@/components/TableComponent.vue'),
    redirect:'/button',
    children:[
      {
        path:'/text',
        component:() => import('@/components/tableComponentChildren/TextComponent.vue')
      },
      {
        path: '/button',
        component:() => import('@/components/tableComponentChildren/ToTextComponent.vue')
      }
    ]
  },
  {
    path: '/404',
    component:() => import('@/components/notFoundComponent/NotFoundComponent.vue')
  },
  {
    path:'*',
    redirect: '/404'
  }
]

路由跳转

使用<router-link></router-link> 可以实现路由地址跳转

下面实现了两个路由间的互相跳转

<template>
<div>
  <h1>员工列表</h1>
  <router-link to="/text">
    <el-button type="primary" size="mini">跳转介绍文本</el-button>
  </router-link>
</div>
</template>

<script>
export default {
  name: "ToTextComponent.vue"
}
</script>

<style scoped>

</style>

<template>
<div>
  <h3>子路由测试文本内容</h3>
  <router-link to="/button">
    <el-button type="primary" size="mini">点击返回</el-button>
  </router-link>

</div>
</template>

<script>
export default {
  name: "TextComponent",
}
</script>

<style scoped>

</style>

函数方式实现路由跳转

jump(url) {
    this.$router.push(url);
}

动态路由

添加路由

从后端获取路由数据,动态的添加到路由列表中

  • 这里直接用i循环取1到5拼接字符串来模拟后端传来的不同路由数据
<script>
export default {
  mounted() {
    //动态添加路由
    for (let i = 1; i <= 5; i++) {
      this.$router.addRoute('main',
          {
            path:`/p${i}`,
            name:`p${i}`,
            component: ()=> import(`@/components/main/children/childrenMenu${i}.vue`)
          }
      );
    }
  },
  name: "mainPage"
}
</script>

第一个参数为父路由name值,第二个参数为子路由对象

重置路由

一般来说,不同用户有不同的路由,但是在注销后我们需要把路由重置为初始的状态,以提供下一个用户登录使用

(Vue2没有提供删除路由的方法,但是在Vue3中提供了)

这里直接初始化路由

export function resetRouter(){
  router.matcher = new VueRouter({routes}).matcher
}
<template>
    <div>
        <el-button size="mini" @click="resetRouter">重置路由</el-button>
    </div>
</template>
<script>
    import {resetRouter} from "@/router/myRouter";
    export default {
        methods:{
            resetRouter(){
                resetRouter();
            }
        }
    }
</script>

刷新页面

由于vue是单页面组件,如果用户刷新页面则相当于重置了vue组件,因此动态路由也会失效

解决方法

我们可以将我们的路由数据存入浏览器,以便于下次页面刷新的时候使用

浏览器为我们提供了两个对象来存放数据

  • localStorage

    • 作用范围:浏览器关闭后依旧保存数据
  • sessionStorage

    • 作用范围:以浏览器标签页为单位,关闭标签页时数据会被清除

例子:

//将数据保存到本地
sessionStorage.setItem("randomInitFerryRes",JSON.stringify(ferryRes));
mounted() {
  //页面加载的时候重新从session域读取
  let randomInitFerryRes = sessionStorage.getItem("randomInitFerryRes");
  if (randomInitFerryRes){
    this.ferryRes = JSON.parse(randomInitFerryRes);
  }
}

导出动态路由js

将刚才的动态路由方法添加到我们的动态路由js文件中作为方法导出

export function addMenuRoutes(){
  //动态添加路由
  for (let i = 1; i <= 5; i++) {
    router.addRoute('main',
        {
          path:`/p${i}`,
          name:`p${i}`,
          component: ()=> import(`@/components/main/children/childrenMenu${i}.vue`)
        }
    );
  }
}

组件数据共享VueX

响应式数据展示技术

设置共享数据

state 用于存储组件之间需要共享的数据

mutations 用于定义方法来更新共享数据信息

  • 方法第一个参数:传入state对象
  • 方法第二个参数:传入更新的值
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    msg:''
  },
  getters: {
  },
  mutations: {
    updateMsg(state,newMsg){
      state.msg = newMsg;
    }
  },
  actions: {
  },
  modules: {
  }
})

修改共享数据

子组件中提供更新数据修改的方法

commit 更新共享数据值

<template>
<div>
  子组件
  <el-input v-model="msg" @change="updateMsg"></el-input>
</div>
</template>

<script>
export default {
  name: "TestChildren",
  methods:{
    updateMsg(){
      this.$store.commit('updateMsg',this.msg);
    }
  },
  data(){
    return {
      msg:''
    }
  }
}
</script>

<style scoped>

</style>

使用VueX生成共享数据更新方法

import {mapMutations} from 'vuex'

methods:{
    ...mapMutations(['updateMsg'])
}

显示共享数据

其它组件获取共享数据

有以下三种方法

  • 直接通过store获取即可
{{$store.state.msg}}
  • 使用计算属性获取
<template>
<div>
  父组件数据:
  {{msg}}
  <router-view></router-view>
</div>
</template>

<script>
export default {
  name: "TestComponent",
  computed:{
    msg(){
      return this.$store.state.msg;
    }
  }
}
</script>

<style scoped>

</style>
  • 使用VueX提供的计算属性函数
    • 数组中提供需要自动获取共享数据的属性名
    • 这种方法针对有大量需要获取的共享数据,可以高效的开发
import {mapState} from 'vuex'

computed: mapState(['msg'])

/*或者如果需要在对象中写入,则使用展开运算符*/

computed: {
    ...mapState(['msg'])
}

异步获取共享数据

一般我们都会从服务器获取数据存入store中以提供共享数据

而mutations中只能包含使得改动立刻生效的代码,不推荐使用await修饰的代码

因此我们使用actions来向服务器发起请求获取共享数据

store中 所有的异步操作 必须放在actions内完成,不能放在mutations中(因为devtools无法监测mutations中的异步)

用actions中的updateMsg2方法为mutations中的方法参数传值

  mutations: {
    updateMsg(state,msg){
      state.msg = msg.msg;
      state.msg2 = msg.msg2;
    }
  },
  actions: {
    updateMsgAction(context){
      context.commit('updateMsg',{
        msg:"异步数据1",
        msg2:"异步数据2"
      });
    }
  },
<template>
<div>
  子组件
  <el-button @click="updateMsgAction"></el-button>
</div>
</template>

<script>
import {mapActions} from 'vuex'
export default {
  name: "TestChildren",
  methods:{
    ...mapActions(['updateMsgAction'])
  },
  data(){
    return {
      msg:'',
      msg2:''
    }
  }
}

mapActions中其实就是生成了 this.$store.dispatch('updateMsg',args) 方法,返回的是promise对象


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com

文章标题:Vue2

字数:5.7k

本文作者:Os467

发布时间:2022-10-10, 22:15:06

最后更新:2022-11-09, 22:20:49

原始链接:https://os467.github.io/2022/10/10/Vue2/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏