vue常用案例

官网

https://cn.vuejs.org/v2/guide/

一、单页面Tab导航栏切换

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="vue.js"></script>
    <style>
        .tab{
            position: relative;
        }
        ul{
            width: 300px;
            display: flex;
            justify-content: space-between;
        }
        ul li{
            border: 1px solid #000;
            padding: 10px;
            list-style: none;
        }
        ul li.active{
            background-color: #ccc;
        }
        .tab div{
            display: none;
            width: 300px;
            height: 300px;
        }
        .tab div.current{
            display: block;
        }
    </style>
</head>
<body>
    <div id="app">
        <div class="tab">
            <ul>
                <!-- 初始化序号与当前点击元素序号相等,添加类名 ,等价于 :style="{backgroundColor: currentIndex == index ? '#ccc': 'initial' }"-->
                <li :class="currentIndex == index ? 'active':''" v-for="(item,index) in list" :key="item.id" @click="click(index)">{{ item.name }}</li>
            </ul>
            <div :class="currentIndex == index ? 'current':''" v-for="(item,index) in bg" :key="item.id" :style="'backgroundColor:' + item.color"></div>
        </div>
    </div>
</body>
<script>
    new Vue ({
        el: "#app",
        data: {
            currentIndex: 0,
            // 初始化序号
            list: [
                {id:1,name:'张三'},
                {id:2,name:'李四'},
                {id:3,name:'王五'},
            ],
            bg: [
                {id:1,color:'red'},
                {id:2,color:'blue'},
                {id:3,color:'pink'},
            ]
        },
        methods:{
            // 点击当前导航栏元素并将当前点击元素序号给初始化序号
            click(i){
                this.currentIndex = i;
            }
        }
    })
</script>
</html>

补充案例:底部导航栏路由和图片切换,同时使用router-link和@click点击图标2次才显示,可使用props和$router.push解决
参考文献:https://www.jb51.net/article/160601.htm

// 子组件
<template>
    <ul class="tabbar-container">
      <li class="tabbar-item" v-for="(item,index) in tabs" :key="index" @click="$router.push(item.path)">
        <i class="tabbar-icon" :style="{backgroundImage: index == currentIndex ? 'url(' + item.reicon + ')' : 'url(' + item.icon + ')',backgroundRepeat: 'no-repeat',backgroundSize: '100%'}"></i>
          {{ item.name }}           
      </li>
    </ul>
</template>

<script>
    export default {
        name: "TabBar",
        props:{
            index: Number
        },
        data() {
            return {
                currentIndex: this.index,
                tabs: [
                    {id:1,name:"首页",active:true,icon:require("../../assets/images/icon/dibu-shouye.png"),reicon:require("../../assets/images/icon/dibu-shouye2.png"),path:"/home"},
                    {id:2,name:"分类",active:false,icon:require("../../assets/images/icon/dibu-fenlei.png"),reicon:require("../../assets/images/icon/dibu-fenlei2.png"),path:"/type"},
                    {id:3,name:"购物车",active:false,icon:require("../../assets/images/icon/dibu-gouwuche.png"),reicon:require("../../assets/images/icon/dibu-gouwuche2.png"),path:"/shopcar"},
                    {id:4,name:"消息",active:false,icon:require("../../assets/images/icon/dibu-xiaoxi.png"),reicon:require("../../assets/images/icon/dibu-xiaoxi2.png"),path:"/message"},
                    {id:5,name:"个人中心",active:false,icon:require("../../assets/images/icon/dibu-gerenzhongxin.png"),reicon:require("../../assets/images/icon/dibu-gerenzhongxin2.png"),path:"/my"}
                ]
            }
        }
    }
</script>

// 父组件
<TabBar class="tabbar" :index='0'></TabBar>

二、模拟后端接口,验证用户名是否可用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="vue.js"></script>
    <style>

    </style>
</head>
<body>
    <div id="app">
        <!-- lazy:失去焦点后触发 -->
        用户名:<input type="text" v-model.lazy="username">{{ tip }}
    </div>
</body>
<script>
    /*步骤:
    1、采用侦听器监听用户名的变化
    2、调用后台接口进行验证
    3、根据验证的结果调整提示信息
    */
    new Vue ({
        el: "#app",
        data: {
            currentIndex: 0,
            username: "",
            tip: ""
        },
        methods:{
            checkName(username){
                // 模拟接口调用
                setTimeout(() => {
                    if (username == "admin") {
                        this.tip = "用户名已存在,请更换一个"
                    }else{
                        this.tip = "用户名可以使用"
                    }
                },2000)
            }
        },
        watch: {
            // 属性名与方法名必须一致
            username(val){
                // 调用后台接口验证用户名的合法性
                this.checkName(val);
                // 修改提示信息
                this.tip = "正在验证……"
            }
        }
    })
</script>
</html>

三、购物车(父子组件传值)

插槽:https://cn.vuejs.org/v2/guide/components-slots.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="vue.js"></script>
    <style>
        .cart-title,.cart-total{
            width: 50%;
            border: 1px solid #000;
            padding: 10px;
            box-sizing: border-box;
        }
        .cart-title{
            margin: 0;
            border-bottom: 0;
            text-align: center;
        }
        .cart-total{
            border-top: 0;
            display: flex;
            justify-content: space-between;
        }
        .cart-list{
            width: 50%;
            text-align: center;
            border-collapse: collapse;
        }
        .cart-list .totalSelect{
            margin-top: -10px;
        }
        .cart-list .btn-group .btn-num{
            width: 20px;
            text-align: center;
        }
    </style>
</head>
<body>
    <div id="app">
        <h3 class="cart-title">我的商品</h3>
        <!-- 绑定属性或方法,前后名称可以不一致 -->
        <car-list :list="cart_list" @del-good="delGood" @change-num="changeNum"></car-list>
        <car-total :list="cart_list"></car-total>
    </div>
    <template id="carList">
        <table class="cart-list" border="1" cellpadding="10" cellspacing="10">
            <tr>
                <th>商品名称</th>
                <th>商品价格</th>
                <th>数量</th>
                <th>操作</th>
            </tr>
            <tr v-for="(item,index) in list" :key="index">
                <td>{{ item.goods_name }}</td>
                <td>{{ item.goods_price }}</td>
                <td class="btn-group">
                    <span class="btn btn-reduce" @click="reduce(index)">-</span>
                    <input class="btn-num" type="text" v-model="item.num" @blur="change(index,$event)"/>
                    <span class="btn btn-add" @click="add(index)">+</span>
                </td>
                <td>
                    <button class="delete" @click="del(index)">删除</button>
                </td>
            </tr>
        </table>
    </template>
    <template id="carTotal">
        <div class="cart-total">
            <div class="total-pay">
                总计:共 {{ list.length }} 件商品,
                合计:{{ totalPrice }} 元
            </div>
            <button class="total-pay-success">去结算</button>
        </div>
    </template>
</body>
<script>
    new Vue ({
        el: "#app",
        data: {
            cart_list: [{
                goods_name: '小米6',
                goods_price: '1699',
                num: '2',
            }, {
                goods_name: '红米3',
                goods_price: '699',
                num: '1',
            }, {
                goods_name: '小米8',
                goods_price: '2899',
                num: '1',
            }],
        },
        methods:{
            // 根据子组件传过来的值中的type,改变数量输入框的值
            changeNum(val){
                // console.log(val)
                if (val.type == 'change') {
                    this.cart_list[val.id].num = val.num;
                }else if (val.type == 'reduce') {
                    if (this.cart_list[val.id].num <= 0) {
                        this.cart_list[val.id].num = 0;
                    }else{
                        this.cart_list[val.id].num--;
                    }
                }else{
                    this.cart_list[val.id].num++;
                }

            },
            // 接收子组件传过来的当前元素的id,删除当前点击元素
            delGood(id){
                this.cart_list.splice(id,1);
            },
        },
        components: {
            carList: {
                template: '#carList',
                data(){
                    return{

                    }
                },
                methods: {
                    // 将当前已减少的输入框数量传给父组件
                    reduce(id){
                        this.emit('change-num',id,'reduce')
                    },
                    // 将当前已增加的输入框数量传给父组件
                    add(id){
                        this.emit('change-num',id,'add')
                    },
                    // 将当前已改变的输入框数量传给父组件
                    change(id,event){
                        this.emit('change-num',id,'change',event.target.value)
                    },
                    // 将当前点击元素id传给父组件
                    del(id){
                        this.emit('del-good',id)
                    },
                    // 封装子组件传值父组件
                    emit(){
                        // console.log(arguments)
                        if (arguments.length > 2) {
                            this.$emit(arguments[0],{
                                id: arguments[1],
                                type: arguments[2],
                                num: arguments[3]
                            })
                        }else{
                            this.$emit(arguments[0],arguments[1])
                        }
                    }
                },
                // 接收父组件传给子组件的值
                props: ['list']
            },
            carTotal: {
                template: '#carTotal',
                computed: {
                    // 计算总价
                    totalPrice(){
                        let total_price = 0;
                        this.list.forEach(item => {
                            total_price += Number(item.goods_price) * Number(item.num);
                        })
                        return total_price;
                    }
                },
                props: ['list']
            }
        }
    })
</script>
</html>

补充:兄弟组件传值
https://www.cnblogs.com/rich23/p/7110409.html

五、异步操作

promise和ajax使用

query(url){
        let p = new Promise((resolve,reject) => {
            Ajax('get',url,null,success,failed);
            // 返回成功数据
            function success(jsondata){
                let json = JSON.parse(jsondata);
                resolve(json);
            }
            // 返回失败数据
            function failed(){
                reject('服务器错误');
            }
        });
        return p;
    };
    query('http://localhost:3000/data')
        .then(res => {
            // 返回成功数据
            console.log(res);
            // 返回下一次异步调用数据
            return query('http://localhost:3000/data2');
        },rej => {
            // 返回失败数据
            console.log(rej);
        })
        .then(res => {
            console.log(res);
            return query('http://localhost:3000/data3');
        },rej => {
            console.log(rej);
        })
        .then(res => {
            console.log(res);
        },rej => {
            console.log(rej);
        })

// Ajax封装
function Ajax(type, url, data, success, failed){
    // 创建ajax对象
    let xhr = null;
    if(window.XMLHttpRequest){
        xhr = new XMLHttpRequest();
    } else {
        xhr = new ActiveXObject('Microsoft.XMLHTTP')
    }
    //转化为大写
    type = type.toUpperCase();
    // 用于清除缓存,防止浏览器缓存
    let random = Math.random();
    // 字符串拼接传过来的数据,如'name=zhangsan&'
    if(typeof data == 'object'){
        var str = '';
        for(var key in data){
            str += key+'='+data[key]+'&';
        }
        data = str.replace(/&$/, '');
    }
    // 判断使用方法
    if(type == 'GET'){
        // 根据是否传入数据处理参数显示
        if(data){
            xhr.open('GET', url + '?' + data, true);
        } else {
            xhr.open('GET', url + '?t=' + random, true);
        }
        xhr.send(null);

    } else if(type == 'POST'){
        xhr.open('POST', url, true);
        // 如果需要像html表单那样POST数据,请使用setRequestHeader() 来添加http头
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xhr.send(data);
    }
    // 检测状态,处理返回数据
    xhr.onreadystatechange = function(){
        //检测是否已经准备好
        if(xhr.readyState == 4){
            //表示响应准备就绪
            if((xhr.status >= 200&&xhr.status<300)||xhr.status==304){
                //请求成功之后的处理
                success(xhr.responseText);
            } else {
                //处理ajax返回异常的情形
                if(failed){
                    failed(xhr.status);
                }
            }
        }
    }
}

fetch使用
https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch

axios使用
post报错400:https://www.cnblogs.com/chenlw/p/9994891.html
传参:https://blog.csdn.net/zhaofuqiangmycomm/article/details/89479904
官方文档:https://www.kancloud.cn/yunye/axios/234846
vue:https://hacpai.com/article/1567922774522
封装:https://www.cnblogs.com/panax/p/10942889.html
https://www.cnblogs.com/chaoyuehedy/p/9931146.html
async/await使用
https://segmentfault.com/a/1190000007535316

四、后台管理路由

vue-router使用
https://router.vuejs.org/zh/installation.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="vue.js"></script>
    <script src="vue-router.js"></script>
    <style>
        *{
            padding: 0;
            margin: 0;
        }
        html,body,#app,.wrapper{
            height: 100%;
        }
        ul{
            list-style: none;
        }
        a{
            text-decoration: none;
            color: #fff;
        }
        .wrapper{
            display: flex;
            flex-direction: column;
            justify-content: space-between;
        }
        .header,.footer,.main .left{
            background-color: #666;
            color: #fff;
        }
        .header,.footer,.main .left ul li{
            text-align: center;
            padding: 10px 0;
        }
        .main{
            height: 100%;
            display: flex;
            justify-content: space-between;
        }
        .main .left{
            width: 20%;
        }
        .main .left ul li{
            border-bottom: 1px solid #fff;
            background-color: #ccc;
        }
        .main .right{
            flex: 1;
            text-align: center;
        }
        .main .right h3{
            padding: 10px 0;
        }
        .main .right table{
            border-collapse: collapse;
            width: 60%;
            margin: 0 auto;
        }
        .main .right table td,.main .right table th{
            padding: 10px 0;

        }
        .main .right table a{
            color: #000;
        }
    </style>
</head>
<body>
    <!-- 要被vue实例控制的区域 -->
    <div id="app">
        <router-view></router-view>
    </div>
</body>
<script>
    // 定义app根组件
    const App = {
        template: `
            <div class="wrapper">
                <header class="header">后台管理系统</header>
                <div class="main">
                    <div class="left">
                        <ul>
                            <li>
                                <router-link to="/users">用户管理</router-link>
                            </li>
                            <li>
                                <router-link to="/rights">权限管理</router-link>
                            </li>
                            <li>
                                <router-link to="/goods">商品管理</router-link>
                            </li>
                            <li>
                                <router-link to="/orders">订单管理</router-link>
                            </li>
                            <li>
                                <router-link to="/settings">用户管理</router-link>
                            </li>
                        </ul>
                    </div>
                    <div class="right">
                        <router-view></router-view>
                    </div>
                </div>
                <footer class="footer">版权信息</footer>
            </div>
        `
    }

    const Users = {
        data(){
            return{
                list: [
                    {id:1,name:"张三",age: 10},
                    {id:2,name:"李四",age: 20},
                    {id:3,name:"王五",age: 30},
                ]
            }
        },
        methods: {
            goDetail(id){
                this.$router.push('/userinfo/' + id)
            }
        },
        template: `
            <div>
                <h3>用户管理区域</h3>
                <table border="1">
                    <thead>
                        <tr>
                            <th>编号</th>
                            <th>姓名</th>
                            <th>年龄</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr v-for="(item,index) in list" :key="item.id">
                            <td>{{ item.id }}</td>
                            <td>{{ item.name }}</td>
                            <td>{{ item.age }}</td>
                            <td>
                                <a href="javascript:;" @click="goDetail(item.id)">详情</a>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
        `
    }

    const UserInfo = {
        // 接收参数
        props: ['id'],
        methods: {
            // 返回
            goBack(){
                this.$router.go(-1)
            }
        },
        template: `
            <div>
                <h5>用户详情页-{{ id }}</h5>
                <button @click="goBack()">后退</button>
            </div>
        `
    }

    const Rights = {
        template: `
            <div>
                <h3>权限管理区域</h3>
            </div>
        `
    }
    const Goods = {
        template: `
            <div>
                <h3>商品管理区域</h3>
            </div>
        `
    }
    const Orders = {
        template: `
            <div>
                <h3>订单管理区域</h3>
            </div>
        `
    }
    const Settings = {
        template: `
            <div>
                <h3>系统管理区域</h3>
            </div>
        `
    }
    // 创建路由对象
    const router = new VueRouter({
        routes: [
            {
                path: '/',
                redirect: '/users',
                component: App,
                children: [
                    {
                        path: '/users',
                        component: Users
                    },
                    {
                        // 传参id
                        path: '/userinfo/:id',
                        component: UserInfo,
                        props: true
                    },
                    {
                        path: '/rights',
                        component: Rights
                    },
                    {
                        path: '/goods',
                        component: Goods
                    },
                    {
                        path: '/orders',
                        component: Orders
                    },
                    {
                        path: '/settings',
                        component: Settings
                    }
                ]
            }
        ]
    })

    new Vue({
        el: "#app",
        router
    })
</script>
</html>

五、登录

token:https://blog.csdn.net/c880420/article/details/80346127
完整案例:https://www.cnblogs.com/web-record/p/9876916.html

// 根目录下vue.config.js 解决跨域
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000/api',
        ws: true,
        changeOrigin: true,
        pathRewrite:{
          '^/api': ''
        }
      },    
    }
  }
}

// main.js
import axios from 'axios'
Vue.prototype.$http = axios
// 添加请求头
axios.interceptors.request.use(config => {
    config.headers.Authorization = window.sessionStorage.getItem('token');
    return config;
})

router.beforeEach((to,from,next) => {
    // 登录页无需权限
    if (to.path == '/login') {
        next()
    }else{
        // 获取token
        const token = window.sessionStorage.getItem('token');
        // token不存在直接跳转登录页
        if(!token) return next('/login');
        // 存在放行
        next();
    }
})

// login.vue
this.$http.post('/api/login',data).then(res => {
    if(res.status !== 200){
         // element-ui弹窗
        this.$message.error('登录失败');
    }
    this.$message.success('登录成功');
     // 缓存token
    window.sessionStorage.setItem('token',res.data.token);
    this.$router.push('/home')
})

// 退出,清缓存
window.sessionStorage.clear();
this.$router.push('/login');

六、增删改查

调用后端接口实现图书管理增删改查

https://github.com/mycummity/book

实现功能

navArray: [
        {
          href: "#navTitle1",
          name: "应用简介"
        }
]

<div
          class="navItem"
          :class="{'selected':currentIndex==index}"
          v-for="(item,index) in navArray"
          :key="index"
          @click="goAnchor(item.href)"
        >
          <span>{{item.name}}</span>
</div>


<div class="appSketch" id="navTitle1">
      
</div>


goAnchor(type) {
      var anchor = this.$el.querySelector(type);
      // chrome
      document.body.scrollTop = anchor.offsetTop - 50;
      // firefox
      document.documentElement.scrollTop = anchor.offsetTop - 50;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,468评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,620评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,427评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,160评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,197评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,334评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,775评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,444评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,628评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,459评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,508评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,210评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,767评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,850评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,076评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,627评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,196评论 2 341

推荐阅读更多精彩内容