Vue Q

一、Vue框架介绍

Vue是一个构建数据驱动的web界面的渐进式框架。

目标是通过尽可能简单的API实现响应式的数据绑定和组合的视图组件。

能够构建复杂的单页面应用。现在我们开始认识一下Vue~

// HTML 页面
<div id="app">
    <span>你的名字是{{name}}</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="../js/main.js"></script>

// main.js 页面
var app = new Vue({
    el: '#app',
    data: {
        name: "Gao_Xin"
    }
});
Vue demo

二、Vue指令

Vue的指令directives很像我们所说的自定义属性,指令是Vue模板中最常用的功能,

它带有v-前缀,功能是当表达式改变的时候,相应的行为作用在DOM上。

<template>
<div>
  <h2>head</h2>
  <p v-text="msg"></p>
  <p v-html="html"></p>
</div>
</template>

<script>
    export default {
        name: "head",
      data(){
          return {
            msg: "消息",
            html: `<h2>插入h2标题</h2>`

          }
      }

    }
</script>

<style scoped>

</style>
v-test v-html
// HTML页面
<div id="app">
        <label><input type="checkbox" v-model="sex" value="male">
            //  <input type="radio" v-model="sex" value="male">
        </label>
        <label><input type="checkbox" v-model="sex" value="female">
        </label>
        {{sex}}
</div>  

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

// main.js 页面

var app = new Vue({
    el: '#app',
    data: {
        // sex: "male",
        sex: [],
    }
});
v-model input
// HTML 页面
<div id="app">
<div>
        <texteare v-model="article">
        </texteare>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="../js/main.js"></script>
// main.js 页面
var app = new Vue({
    el: '#app',
    data: {
        // sex: "male",
        sex: [],
        article: "这是一段文本。。这是一段文本。。这是一段文本。。这是一段文本。。这是一段文本。。"
    }
});
v-model textarea
// HTML页面
<div id="app">
        <!--<select v-model="from">-->
            <!--<option value="1">单选1</option>-->
            <!--<option value="2">单选2</option>-->
        <!--</select>-->
        <!--{{from}}-->
        <select v-model="where" multiple="">
            <option value="1">多选1</option>
            <option value="2">多选2</option>
            <option value="3">多选3</option>
        </select>
        {{where}}
 </div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="../js/main.js"></script>
// main.js 页面
var app = new Vue({
    el: '#app',
    data: {
        from: null,
        where: [],
    }
});
v-model select
//  需求是展示一个人的所有爱好 以及喜欢吃的食物和价格
// HTML 页面  
<div id="app">
<div> 
        <span>你的爱好是</span>
        <ul>
            <li v-for="hobby in hobby_list">{{hobby}}</li>
        </ul>
</div>
<div>
        <span>你喜欢的食物</span>
        <ul>
            <li v-for="food in food_list">{{food.name}}: 价格¥{{food.discount ? food.price*food.discount: food.price}}</li>
        </ul>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="../js/main.js"></script>
// main.js
var app = new Vue({
    el: '#app',
    data: {
        hobby_list: ["王者毒药","LOL","吃鸡"],
        food_list: [
            {
                name: "葱",
                price: 5,
                discount: .8,
            },
            {
                name: "姜",
                price: 8,
                // discount: .5

            }
        ],
    }
});
v-for
// 我们常常见到博客中评论回复的时候 @某某某
// 我们点击这个用户是能够跳转的  那么这个应该是个动态的
// 我们就需要给标签属性进行动态绑定 v-bind
// HTML页面
<style>
        .active {
            background: red;
        }
</style>
<div id="app">
   <div>
        <!--<a v-bind:href="my_link">点我</a>-->
        <a v-bind:href="my_link" :class="{active: isActive}">点我 
 </a>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="../js/main.js"></script>
// main.js
var app = new Vue({
    el: '#app',
    data: {
        my_link: "http://baidu.com",
        isActive: true,
    }
});
v-bind
// 那我们以前的事件绑定在vue中是怎么做到的呢
// HTML页面
<div>
        <span>事件</span>
        <button v-on="{mouseenter: onMouseenter, mouseleave: onMouseleave}" v-on:click="onClick(1)">点我</button>
    </div>
    <div>
        <input type="text" v-on="{keyup: onKeyup}">
</div>
// main.js
var app = new Vue({
    el: '#app',
    data: {
        my_link: "http://baidu.com",
        isActive: true,
    },
    methods: {
        onClick: function (a) {
            alert(a)
        },
        onMouseenter: function () {
            console.log("mouse enter")
        },
        onMouseleave: function () {
            console.log("mouse leave")
        },
        onKeyup: function () {
            console.log("key up")
        }
    },
});
v-on
// 我们之前写过权限 我们现在模拟一个简单的权限
// HTML页面
<div>
        <div v-if="role == 'admin' || 'super_admin'">管理员你好</div>
        <div v-else-if="role == 'hr'">待查看简历列表</div>
        <div v-else> 没有权限</div>
</div>
// main.js 页面
var app = new Vue({
    el: '#app',
    data: {
        role: 'admin',
       // role: 'hr',
    },
});
v-if v-else-if v-else
// 现在我们要实现一个功能 点击一个按钮显示文本 再点击取消
// 这种显示和隐藏的功能 vue也帮我们进行了封装
// HTML页面
<div id="app">
    <button @click="on_click()">
        点我
    </button>
    <p v-show="show">提示文本</p>
</div>
// main.js 页面
var app = new Vue({
    el: '#app',
    data: {
        show: false,
    },
    methods: {
        on_click: function () {
            this.show = !this.show
        }
    }

});
v-show
//  我们现在要获取用户的注册信息
//  用户名以及手机号 用指令修饰符能够让我们更加便捷
// HTML 页面
<div>
    用户名: <input type="text" v-model.lazy.trim="username"><br>
    {{username}}
    手机号: <input type="text" v-model.number="phone"><br>
    {{phone}}
</div>
// main.js 页面
var app = new Vue({
    el: '#app',
    data: {
        username: "",
        phone: "",
    },
});
指令修饰符
// 我们现在有个需求 点击元素 让这个元素在浏览器窗口钉住
// 我们只需自定义一个指令,只要给元素绑定指令就可以
// HTML页面
<style>
        .card {
            border: solid 3px red;
             200px;
            height: 100px;
            background: #eeeeee;
            margin-bottom: 5px;
        }
</style>
<div id="app">
 <div class="card" v-pin:true.bottom.right="pinned">
        <button @click="pinned = !pinned">盯住/取消</button>
        这是一个盒子文本内容
    </div>
    <div class="card">
        这是一个盒子文本内容
    </div>
    <div class="card">
        这是一个盒子文本内容
    </div>
    <div class="card">
        这是一个盒子文本内容
    </div>
    <div class="card">
        这是一个盒子文本内容
    </div>
    <div class="card">
        这是一个盒子文本内容
    </div>
    <div class="card">
        这是一个盒子文本内容
    </div>
</div>
// main.js 页面
Vue.directive('pin', function (el, binding) {
    var binded = binding.value;
    var position = binding.modifiers;
    var warning = binding.arg;
    console.log(position)
    if(binded){
        el.style.position = 'fixed';
        for(var key in position){
            if (position[key]){
                el.style[key] = '20px';
            }
        }
        if (warning === 'true'){
            el.style.background = '#666'
        }

    }
    else {
        el.style.position = 'static';
        el.style.background = "#eeeeee"
    }
});
new Vue({
    el: '#app',
    data: {
        pinned: false,
    }
});
自定义指令以及修饰符和参数

Vue获取DOM元素

    <style>
        .box {
             200px;
            height: 200px;
            border: solid 1px red;
        }
    </style>

</head>
<body>
<div id="app">
    <div  class="box" ref="my_box">
        这是一个盒子
    </div>
</div>

<script>
    const app = new Vue({
        el: "#app",
        mounted(){
            this.$refs.my_box.style.color = "red";
        }
    })
</script>

</body>
获取DOM

Vue计算属性

我们的模板表达式非常的便利,但是逻辑复杂的时候,模板会难以维护,vue提供了计算属性。

我们用方法也能达到效果,那么我们为什么要用计算属性呢~

其实在vue中方法和计算属性达到的效果是一样的,但是计算属性是基于依赖进行缓存的,

只有依赖的数据发生改变的时候,才会重新执行计算属性的函数,每次调用都会从缓存中拿之前算好的数据。

而方法是每调用一次,执行一次。

// 现在我们有一个成绩的表格 来计算总分和平均分
// HTML页面
<div id="app">
    <table border="1">
        <thead>
            <th>学科</th>
            <th>分数</th>
        </thead>
        <tbody>
            <tr>
                <td>数学</td>
                <td><input type="text" v-model.number="math"></td>
            </tr>
            <tr>
                <td>物理</td>
                <td><input type="text" v-model.number="physics"></td>
            </tr>
            <tr>
                <td>英语</td>
               <td><input type="text" v-model.number="english"></td>
            </tr>
            <tr>
                <td>总分</td>
               <!--<td>{{math+physics+english}}</td>-->
                <td>{{sum}}</td>
            </tr>
            <tr>
                <td>平均分</td>
               <!--<td>{{Math.round((math+physics+english)/3)}}</td>-->
                <td>{{average}}</td>
            </tr>
        </tbody>
    </table>
</div>
// js 页面
var app = new Vue({
    el: '#app',
    data: {
        math: 90,
        physics:88,
        english: 78,
    },
    computed: {
        sum: function () {
            var total = this.math + this.physics + this.english
            return total
        },
        average: function () {
            var average_num = Math.round(this.sum/3)
            return average_num
        }
    }
});
计算属性 computed 

Vue过滤器

过滤器是在数据到达用户的最后一步进行简单的过滤处理,复杂的还是要用计算属性或者方法。

// 我们两个需求 一个是价格展示后面自动加“元”
//  单位 毫米和米的转换
// HTML页面
<div>
        <p>价格展示</p>
        <input type="text" v-model="price">
        {{price | currency('USD')}}
</div>
<div>
        <p>换算</p>
        <input type="text" v-model="meters">
        {{meters | meter}}
 </div>
// js 代码
Vue.filter('currency', function (val, unit) {
    val = val || 0;
    var ret = val+ unit
    return ret
});

Vue.filter('meter', function (val) {
    val = val || 0;
    return (val/1000).toFixed(2) + "米"
});
new Vue({
    el: '#app',
    data: {
        price: 10,
        meters: 10,
    }
});
过滤器 filter

三、Vue组件

组件 (Component) 是 Vue.js 最强大的功能之一。

组件可以扩展 HTML 元素,封装可重用的代码。是可复用的Vue实例。

组件的注册

// html 代码
<div id="app">
  <my-component></my-component>
</div>
// js 代码
Vue.component('my-component', {
  template: '<div>A component!</div>'
})
var app = new Vue({
  el: '#app',
  data: {
       
  } 
});
全局注册
// html 代码
<div id="app">
  <my-component></my-component>
</div>
// js 代码
// 组件中的data必须是个函数
var Child = {
  template: '<div>A component!</div>',
  data: function() {
      return {
            name: "gao",
      }
}};

new Vue({
  // ...
  components: {
    // <my-component> 将只在父组件模板中可用
    'my-component': Child
  }
})
局部注册
// js 代码
Vue.component('child', {
    template: `<div><button @click="on_click()">{{msg}}</button></div>`,
    data: function () {
        return {
            msg: "点我",
        }
    },
    methods: {
        on_click(){
            alert(123)
        }
    }
});
new Vue({
    el: "#app",
})
组件中的data methods
<script>
    var my_component = {
        template: `<div><h1>{{msg}}</h1></div>`,
        data(){
            return{
                msg: "这是子组件"
            }
        }
    };
    var global_component = {
        template: `<div>
                        <h1>{{msg}}</h1>
                        <button @click="on_click">点我</button>
                        <my_component></my_component>
                    </div>`,
        data(){
            return {
                msg: "全局组件"
            }
        },
        methods: {
            on_click() {
                alert("123")
            }
        },
        components:{
            my_component:my_component,
        }
    };
    const app = new Vue({
        el: "#app",
        data: {

        },
        components: {
            global_component: global_component,
            // my_component: my_component,
        }
    });


</script>
子组件的注册

组件之间的通信

我们的组件在任何地方用的时候都要是一个样子么~

可不可以我们给组件传个参数~让组件在不同的地方表现不同的状态~

我们之前说过博客评论@某某某,点击用户名可以跳转到该用户站点。

这样一个小功能,我们每次@的时候都要写,我们可以封装成组件,传值即可~~

// html 代码
<div id="app">
    <child username="gaoxin"></child>
</div>
// js 代码
Vue.component('child', {
    template: `<a :href="'/user/'+ username">{{username}}</a>`,
    props: ["username"],
});


var app = new Vue({
    el: "#app",
    data:{
        name: "@gaoxin"
    }

});
父子通信

app.$on(event, callback) 监听当前实例上的自定义事件,事件由$emit触发,回调函数接收事件触发器额外参数。

app.$emit(event, [args....])  触发当前实例上的事件,额外参数传给监听器的callback回调函数。

// html 代码
<div id="app">
    <parent></parent>
</div>
// js 代码
Vue.component('parent',{
    template: `
        <div>
            <child @show_balance="show"></child>
            <p v-if="active">您的余额998</p>
        </div>
    `,
    data: function () {
        return {
            active: false,
        }
    },
    methods: {
        show: function(data){
            this.active=true;
            console.log(data)
        }
    }

});
Vue.component('child', {
    template: `<div><button @click="on_click()">{{msg}}</button></div>`,
    data: function () {
        return {
            msg: "显示余额",
        }
    },
    methods: {
        on_click(){
            // alert(123)
            this.$emit('show_balance', {q:1,b:2})
        }
    }
});
子父通信

平行组件之间的通信,喊话需要一个中间调度器,在组件加载完成之后去监听调度器事件,回调函数接收数据。

// html 代码
<div id="app">
    <whh></whh>
    <shh></shh>
</div>
// js 代码
var Event = new Vue()

Vue.component('whh',{
    template: `
        <div>
            我说: <input @keyup="on_change" v-model="i_said">
        </div>
    `,
    data: function () {
        return {
            i_said: '',
        }
    },
    methods: {
        on_change: function () {
            Event.$emit("whh_said_something", this.i_said)
        }
    }
});
Vue.component('shh', {
    template: `
        <div>
            花花说:{{whh_said}}
        </div>
    `,
    data: function () {
        return {
            whh_said: '',
        }
    },
    mounted: function () {
        var me = this
        Event.$on('whh_said_something', function (data) {
            me.whh_said = data
        })
    }
});
非父子组件通信

混合Mixins

重复功能和数据的储存器,可以覆盖Mixins的内容。

// 点击显示和隐藏  提示框的显示和隐藏
// html 代码
<div id="app">
    <PopUp></PopUp>
    <ToolTip></ToolTip>
</div>
// js 代码
var base = {
     data: function () {
        return {
            visible: false,
        }
    },
    methods: {
        show: function () {
            this.visible = true
        },
        hide: function () {
            this.visible = false
        }
    }
}

Vue.component('popup', {
    template:`
        <div>
        <button @click="show">PopUp show</button>
        <button @click="hide">PopUp hide</button>
        <div v-if="visible"><p>hello everybody</p></div>
        </div>
    `,
    mixins: [base],
    data: function () {
        return {
            visible: true,
        }
    }

});
Vue.component('tooltip', {
    template: `
        <div>
        <div @mouseenter="show" @mouseleave="hide">ToolTip</div>
        <div v-if="visible"><p>ToolTip</p></div>
        </div>
    `,
    mixins: [base]
});

new Vue({
    el: "#app",
})
Mixins

插槽 Slot

插槽是一套内容分发的API,在组件中,<slot>作为内容承载分发的出口

// html 代码
<div id="app">
    <panel>
        <div slot="title"> HELLO</div>
        <div slot="content">hello</div>
        
    </panel>
    <panel></panel>
    <panel></panel>
</div>

<template id="panel-tpl">
    <div class="panel">
        <div class="title">
            <slot name="title"></slot>
        </div>
        <div class="content">
            <slot name="content"></slot>
        </div>
        <!--<div class="content">Failure is probably the fortification in your pole. It is like a peek your wallet as the thief, when you are thinking how to spend several hard-won lepta,</div>-->
        <div class="footer">
            <slot name="footer">更多信息</slot>
        </div>
    </div>
</template>
// js 代码
Vue.component('panel', {
    template: '#panel-tpl',

});

new Vue({
    el: "#app",
})
Slot

四、vue-router

vue-router是Vue的路由系统,定位资源的,我们可以不进行整页刷新去切换页面内容。

vue-router的安装和基本配置

vue-router.js 可以下载 也可以用cdn,基本配置信息看如下代码~~~

// html 代码
<div id="app">
    <div>
        <router-link to="/">首页</router-link>
        <router-link to="/about">关于我们</router-link>
    </div>
    <div>
        <router-view></router-view>
    </div>

</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script src="../js/router_demo.js"></script>
// js 代码
var routes = [
    {
        path: "/",
        component: {
            template: `<div><h1>首页</h1></div>`
        }
    },
    {
        path: "/about",
        component: {
            template: `<div><h1>关于我们</h1></div>`
        }
    }
]

var router = new VueRouter({
    routes: routes,
    // 路由去掉#
    // mode: 'history',
});

var app = new Vue({
    el: '#app',
    router: router,
});
vue-router demo

路由的一些方法

路由传参以及获取参数~~

// html 代码
<div id="app">
    <div>
        <router-link to="/">首页</router-link>
        <router-link to="/about">关于我们</router-link>
        <router-link to="/user/琴女?age=20">琴女</router-link>
        <router-link to="/user/提莫">提莫</router-link>
    </div>
    <div>
        <router-view></router-view>
    </div>
</div>
// js 代码
var routes = [
    {
        path: "/",
        component: {
            template: `<div><h1>首页</h1></div>`
        }
    },
    {
        path: "/about",
        component: {
            template: `<div><h1>关于我们</h1></div>`
        }
    },
    {
        path: "/user/:name",
        component: {
            template: `<div>
                      <h1>我是:{{$route.params.name}}</h1>
                      <h1>我年龄是:{{$route.query.age}}</h1>
                    </div>`,
        }
    }
]

var router = new VueRouter({
    routes: routes,
});

var app = new Vue({
    el: '#app',
    router: router,
});
传参以及获取参数

命名路由~  注意router-link里to一定要v-bind~~

// html代码
<div id="app">
    <div>
        <router-link to="/">首页</router-link>
        <router-link :to="{name: 'about'}">关于我们</router-link>
        <router-link to="/user/gaoxin?age=19">gaoxin</router-link>
    </div>
    <div>
        <router-view></router-view>
    </div>
</div>
// js代码
let routes = [
        {
            path: '/',
            component: {
                template: `<h1>这是主页</h1>`
            }
        },
        {
            path: "/about",
            name: "about",
            component: {
                template: `<h1>关于我们</h1>`
            }
        },
        {
            path: "/user/:name",
            component: {
                template: `<div>
                            <h1>我是{{$route.params.name}}</h1>
                           <h2>我的年龄是{{$route.query.age}}</h2>
                            </div>
                          `
            }
        }
    ];

    let router = new VueRouter({
        routes: routes,
        mode: "history"
    });

    const app = new Vue({
        el: "#app",
        router: router,
        mounted(){
            console.log(this.$route)
            console.log(this.$router)
        }
    })
命名路由

子路由~~ 以展示详细为例~~

// 添加子路由变化的只有父级路由 
// 基于上面的例子增加
// js 代码
{
        path: "/user/:name",
        component: {
            template: `<div>
                      <h1>我是:{{$route.params.name}}</h1>
                      <h1>我年龄是:{{$route.query.age}}</h1>
                      <router-link to="more" append>更多信息</router-link>
                      <router-view></router-view>
                    </div>`,
        },
        children: [
            {
            path: "more",
            component: {
                template: `<div>
                      {{$route.params.name}}的详细信息
                </div>`,
            }
        }
        ]

    },
子路由

 手动访问路由,以及传参~~

// 基于上面例子追加
// html 代码
<div id="app">
    <div>
        <router-link to="/">首页</router-link>
        <router-link to="/about">关于我们</router-link>
        <router-link to="/user/琴女?age=20">琴女</router-link>
        <router-link to="/user/提莫">提莫</router-link>
        // 添加一个button按钮
        <button @click="on_click">旅游</button>
    </div>
    <div>
        <router-view></router-view>
    </div>
</div>    
// js 代码
// 注意路由name的使用 这是在原例子追加
var app = new Vue({
    el: '#app',
    router: router,
    methods: {
        on_click: function () {
            setTimeout(function () {
                this.$router.push('/about')
                setTimeout(function () {
                    this.$router.push({name: "user", params:{name: "琴女"},query:{age: 20}})
                }, 2000)
            }, 2000)
        }
    }
});
手动路由~以及传参

命名路由视图 router-view

当我们只有一个<router-view></router-view>的时候~所有内容都展示在这一个面板里面~

如果是content 和 footer 就需要同时显示并且不同区域~这就需要对视图进行命名~

// html 代码
<div id="app">
    <div>
        <router-link to="/">首页</router-link>
        
    </div>
    <div>
        <router-view name="content" class="content-view"></router-view>
        <router-view name="footer" class="footer-view"></router-view>
    </div>

</div>
// js 中的主要代码
var routes = [
    {
        path: "/",
        components: {
            content: {
                template: `<div><h1>首页</h1></div>`,
            },
            footer: {
                template: `<div><h1>关于我们</h1></div>`,
            }
        }
    },
]
命名路由视图

错误路由的重定向~~

let routes = [
     {
            path: "**",
            redirect: "/"
        }   
]
redirect

$route以及$router的区别~~

  -- $route为当前router调转对象,里面可以获取name, path, query, params等~

  -- $router为VueRouter实例,有$router.push方法等~~

路由的钩子

路由的生命周期就是从一个路由跳转到另一路由整个过程,下面介绍两个钩子~

router.beforeEach()   router.afterEach()  详情请看代码~~

// html 代码
<div id="app">
    <router-link to="/">首页</router-link>
    <router-link to="/login">登录</router-link>
    <router-link to="/user">用户管理</router-link>
    <div>
        <router-view></router-view>
    </div>
</div>
//  js 代码
var routes = [
    {
        path: "/",
        component: {
            template: "<h1>首页</h1>"
        }
    },
    {
        path: "/login",
        component: {
            template: "<h1>登录</h1>"
        }
    },
    {
        path: "/user",
        component: {
            template: "<h1>用户管理</h1>"
        }
    }
];
var router = new VueRouter({
    routes: routes
});

router.beforeEach(function (to,from,next) {
    // console.log(to)
    // console.log(from)
    // console.log(next)
    // next(false)
    if(to.path=="/user"){
        next("/login")
    }
    else {
        next();
    }
});
router.afterEach(function (to, from) {
    console.log(to)
    console.log(from)
});

var app = new Vue({
    el: '#app',
    router: router
});
路由钩子
next:function  一定要调用这个方法来resolve这个钩子函数。
        执行效果依赖next方法的调用参数
        next() 什么都不做继续执行到调转的路由
        next(false) 中断当前导航 没有跳转 也没有反应
        next("/")  参数是路径 调转到该路径
        next(error)  如果next参数是一个Error实例 导航终止该错误
                    会传递给router.onError()注册过的回调中
next 参数详解

上面的例子~~如果/user下面还有子路由的情况下会怎么样呢~????

// 匹配子路由 改一下匹配方法就可以~
// js 改动代码
router.beforeEach(function (to,from,next) {
    // console.log(to)
    // console.log(from)
    // console.log(next)
    // next(false)
    if(to.matched.some(function (item) {
            return item.path == "/post"
        })){
        next("/login")
    }
    else {
        next();
    }
});
// 元数据配置 改动代码
// html 部分
 {
        path: "/user",
        meta: {
            required_login: true,
        },
        component: {
            template: `
                <div>
                <h1>用户管理</h1>
                <router-link to="vip" append>vip</router-link>
                <router-view></router-view>
                </div>
                `
        },
        children: [{
            path: "vip",
            meta: {
              required_login: true,
            },
            component: {
                template: '<h1>VIP</h1>'
            }
        }]
    }
// js 部分
router.beforeEach(function (to,from,next) {
    // console.log(to)
    // console.log(from)
    // console.log(next)
    // next(false)
    if(to.meta.required_login){
        next("/login")
    }
    else {
        next();
    }
});
匹配子路由以及元数据配置

五、vue生命周期简介

生命周期的钩子 LifeCycle hooks

上面已经能够清晰的看到vue2.0都包含了哪些生命周期的钩子函数~~

那么 执行顺序以及什么时候执行,我们上代码来看~~~

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>

</head>
<body>
<div id="app">
    <p>{{message}}</p>
</div>


<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var app = new Vue({
        el: "#app",
        data: {
            message: "hello gaoxin!!"
        },
        beforeCreate: function () {
            console.group("beforeCreate  创建实例之前===========");
            console.log("el: " + this.$el); // undefined
            console.log("data: " + this.$data); // undefined
            console.log("message: " + this.message);
        },
        created: function () {
            console.group("Created:  创建实例完成===========");
            console.log("el: " + this.$el); // undefined
            console.log("data: " + this.$data); // 已被初始化
            console.log("message: " + this.message); // 已被初始化
        },
        beforeMount: function () {
            console.group("beforeMount  挂载前状态==========");
            console.log("el: " + this.$el); // 已被初始化
            console.log("data: " + this.$data); // 已被初始化
            console.log("message: " + this.message); // 已被初始化
        },
        mounted: function () {
            console.group("Mounted  挂载结束状态=============");
            console.log("el: " + this.$el); // 已被初始化
            console.log("data: " + this.$data); // 已被初始化
            console.log("message: " + this.message); // 已被初始化
        },
        beforeUpdate: function () {
            console.group("beforeUpdate  更新前状态=========");
            console.log("el: " + this.$el);
            console.log("data: " + this.$data);
            console.log("message: " + this.message);
        },
        updated: function () {
            console.group("Updated  更新完成状态");
            console.log("el: " + this.$el);
            console.log("data: " + this.$data);
            console.log("message: " + this.message);
        },
        beforeDestroy: function () {
            console.group("beforeDestroy  实例销毁之前");
            console.log("el: " + this.$el);
            console.log("data: " + this.$data);
            console.log("message: " + this.message);
        },
        destroyed: function () {
            console.group("Destoryed  实例销毁之后");
            console.log("el: " + this.$el);
            console.log("data: " + this.$data);
            console.log("message: " + this.message);
        }

    })
</script>


</body>
</html>
生命周期钩子函数

create 和 mounted 相关

执行上面代码,可以看到:

  beforecreated :el 和 data 并未初始化

  created:完成了data数据的初始化 el 没有

  beforeMount:完成了el 和 data的初始化

  mounted:完成了挂载

也就是说~挂载前的状态是虚拟DOM技术,先把坑站住了~挂载之后才真正的把值渲染进去~

update 相关

我们在浏览器console里执行命令:

  app.message = "hello~"

我们就出发了update相关的钩子函数~也就是说data里的值被修改会出发update的操作~

destroy 相关

我们在浏览器console里执行命令:

  app.$destroy();

触发了destroy相关的钩子函数,也就是说组件被销毁~

更改message的值~DOM中的值不变~也就是说DOM元素依然存在只是不受vue控制了~~

六、Node.js  npm

什么是Node.js  以及npm

简单的来说 Node.js 就是运行在服务端的JavaScript,基于Chrome V8 引擎的。

npm 是Node.js 的包管理工具。

npm的安装和更新

Node.js下载安装   Node.js 官网下载安装。npm自带的包管理工具。

查看安装版本信息:

  --  node -v  查看Node.js 版本信息

  --  npm -v  查看npm版本信息

更新npm到指定版本:

  --  npm install npm@5.3.0 -g

  -- npm install npm@latest -g 更新最新的稳定版本

npm 常用操作

之前我们用JQuery或者Bootstrap用cdn 或者直接手动下载并放入项目,而且要管理版本。

有了npm,我们管理自己的依赖包以及版本更加简单。

到自己项目目录下,进行以下命令:

  -- npm init -y    输入-y使用默认配置项 生成package.json文件。

  -- npm i jquery@0.0.0   简写install 为 i 下载依赖  不写@ 默认最新版本

  -- npm uninstall jquery  卸载依赖包

  -- npm update jquery   更新依赖包

  -- npm list  列出已安装的依赖

  -- npm install webpack --D 保存为开发环境依赖

  -- 老版本需要 --save 参数 现在不需要了

我们的项目目录下会生成一个 node_modules 目录,我们用npm下的包会在这个目录下。

我们所有的依赖信息放在package.json文件中,包括我们所有的依赖以及版本。

如果我们删掉 node_modules目录,可以使用 npm i  来下载所有依赖。

npm 常用配置项

当我们用npm init 的时候用了参数 -y,如果不用-y我们可以进行一些配置。

在我们的package.json文件中有很多配置项

  -- name  项目名字 中间不能有空格只能用小写

  -- version  项目版本

  -- description   项目描述信息

  -- main  项目的入口文件

  -- scripts 指定命令的快捷方式 npm run test     test是scripts里的键名 值为具体命令

  -- author 作者

  -- license  许可证

  -- dependencies  生成环境依赖的包以及版本信息

  -- devDependencies  开发环境的依赖

webpack3

webpack是什么

webpack是一个模块打包器,它将根据模块的依赖关系进行静态分析,

然后将这些模块按照指定的规则生成静态资源。

那么,我们为什么要用这个东西呢~~因为前端的包袱太多,历史遗留问题太重~~~脑补吧......

安装和配置

webpack是跑在Node.js环境下的,所以确定自己有node环境。

安装方式:

  -- npm install webpack -g  全局安装

  -- webpack  <要打包文件>  <打包后文件>     全局这种方式进行打包

  -- npm install webpack   在自己的项目下 npm init 后在下载webpack 这就是局部安装

  -- node_modules/.bin/webpack <要打包文件>  <打包后文件>   项目里要打包文件是入口文件

  -- 路径太长 太烦 可以自定义命令  在package.json 文件的 scripts下面自定义

entry 和 output

entry 入口文件  output 出口文件

上面我们自定义命令的时候 命令太长了~~而且我们命令太多的时候我们需要每次都自定义多条命令~~

我们可以把命令写在webpack.config.js文件中~~

module.export = {
    // 所有的入口文件
    entry: {
         home: './main.js', 
         login: './login.js',
    }, 
    // 出口文件  
    output: {
         filename: '[name].bundle.js',
         path: __dirname + '/dist',
    }       
}
// backage.json  下的scripts
scripts: {
     "pack": "node_moudles/.bin/webpack --watch"
}

//  运行命令
npm  run pack
webpack.config.js

webpack4

webpack的新特性

1, webpack不在单独使用,需要webpack-cli

  -- 全局安装  npm install webpack webpack-cli -g -D

  -- 局部安装  npm install webpack webpack-cli -D

2, 增加了模式区分 (development, production)

  --webpack --mode development/production 进行模式切换

  -- development 开发者模式 打包默认不压缩代码

  -- production  生产者模式 上线时使用,压缩代码。 默认是这个模式

3,固定入口目录为src,与入口默认文件index.js,打包后文件在新增的dist目录下

  -- 当只有一个入口文件也就是src/index.js时,无需增加webpack.config.js

4,多入口以及多出口

entry: {
    // 多入口
    a: "./src/js/index.js",
    b: "./src/js/index2.js",
}
output: {
    // 多出口
    path: path.resolve(__dirname, 'dist'),
    filename: './js/[name].bundle.js'
}
webpack.config.js  

vue-cli

vue-cli是官方提供的快速构建这个单页面应用的脚手架。

根据官方文档中的构件流程:

  -- 前提是已经安装了node.js 否则npm都用不了

  -- 1,使用npm全局安装vue-cli 

      npm install -g vue-cli

  -- 2, 安装完成后在自己的工作空间里

      vue init webpack vue-demo  

    输入命令后进入安装阶段,需要用户输入一些信息 这里省略了.....

  -- 3,切换到我们的项目目录下 

      cd vue-demo

      npm run dev

目录结构:

  -- build 里面是一些操作文件,使用npm run * 时其实执行的就是这里的文件

  -- config 配置文件,执行文件需要的配置信息

  -- src 资源文件 所有的组件以及所有的图片 都在这个文件夹下

  -- node_modules  项目依赖包

  -- static 静态资源

  -- package.json   依赖包的json文件

其实这里面命令很多~我们在后续项目中应用到再说~~~

vue-cli配置JQuery、bootstrap

第一步  下载安装

  -- npm install jquery 

  -- npm install bootstrap 

第二步 修改build/webpack.base.conf.js

const webpack = require('webpack')
// 在module.exports里添加插件
plugins: [
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery",
      "windows.jQuery": "jquery",
      // Popper: ['popper.js', 'default']
    })
],
//  *******下面是如果手动下载bootstrap用的*******
resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      // 如果是手动下载的bootstrap需要添加这个配置
      // 'assets': path.resolve(__dirname, '../src/assets'),
      // 'jquery': 'jquery/src/jquery'
    }
  },
修改配置文件

第三步 修改主程序的js文件 main.js

import $ from 'jquery'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.min.js'
main.js 

vue-cli 3.0

第一步 下载vue-cli 3.0

  -- npm install -g @vue/cli

  -- 报错 npm error  可以运行下面命令

    -- npm cache clean --force && npm cache verify

第二步 创建项目

  -- vue create xxxx

  之后会出现很多选项,我们可以根据自己的习惯去选择~~

第三步 目录结构以及配置文件

  -- vue-cli3 目录更加简单

  -- 我们手动在项目根目录下创建  vue.config.js 里面写vue的配置信息

vue-cli3 配置jQuery、bootstrap

  -- 跟vue-cli2一样的配置,手动创建一个webpack.base.conf.js 

七、Vuex简介

vuex是一个专门为Vue.js设计的集中式状态管理架构。

状态? 我们把它理解为在data中需要共享给其他组件使用的部分。

Vuex和单纯的全局对象有以下不同:

1、Vuex 的状态存储是响应式的。当vue组件从store中读取状态的时候,

  若store中的状态发生变化,那么相应的组件也会相应的得到高效更新。

2、你不能直接改变store中的状态。改变store中的状态的唯一途径就是显示的

  提交(commit)mutation。这样使得我们可以方便的跟踪每一个状态的变化,

  从而让我们能够实现一些工具来帮助我们更好的了解我们的应用。

安装使用vuex

  --  npm install vuex

// main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import vuex from 'vuex'

Vue.use(vuex)

Vue.config.productionTip = false

const store = new vuex.Store({
    state: {
      show: false,
    }
});

new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
});
vuex的使用一
// 为了方便维护,我们通常把在src下面新建一个store文件夹,
// 然后在里面新建一个index.js
import Vue from 'vue'
import Vue_x from "vuex"

Vue.use(Vue_x);

export default  new Vue_x.Store({
   state: {
      show: false,
    },
});
// 那么main.js要改成
import Vue from 'vue'
import App from './App'
import router from './router'
import store from "./store"

Vue.config.productionTip = false;

new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
});
vuex的使用二

State

简而言之~~state是保存我们data中需要共享的数据。

由于Vuex的存储是响应式的,从store实例中读取状态的最简单的方式就是在计算属性中返回某个状态。

this.$store.state.count

// 创建一个组件
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count(){
      return this.$store.state.count
    }
  }
};
组件中获取vuex中状态

Getter

有时候我们需要从store中的state中派生出一些状态,例如对数据进行简单的计算。

并且很多组件都需要用到此方法,我们要么复制这个函数,要么抽取到一个公共函数,多处导入。

我们vuex提供了更加方便的方法,getter ,它就像计算属性一样,getter的返回值会根据它的依赖被

缓存起来,只有它的依赖发生改变时,才会重新计算。

Getter会接收state作为其第一个参数:

import Vue from 'vue'
import Vue_x from "vuex"

Vue.use(Vue_x);

export default  new Vue_x.Store({
   state: {
     count: 20,
   },
  // 通过 this.$store.getters.my_func
  getters: {
    my_func: function (state) {
      return state.count * 2
    }
  },

});
Getter使用

Getter也可以接收getters为第二个参数:

import Vue from 'vue'
import Vue_x from "vuex"

Vue.use(Vue_x);

export default  new Vue_x.Store({
   state: {
     count: 20,
   },
  // 通过 this.$store.getters.my_func
  getters: {
    my_func: function (state) {
      return state.count * 2
    },
    // 通过 this.$store.getters.my_func_count
    my_func_count: function (state, getters) {
      return getters.my_func.length
    }
  },

});
Getter使用

Mutation

更改Vuex中的store中的状态的唯一方法是提交mutation。

每个mutation都有一个字符串的事件类型(type),和一个回调函数handler。

也就是说我们要触发mutation中定义的方法(type),然后才会执行这个方法(handler)。

这个方法就是我们更改状态的地方,它会接收state为第一个参数,后面接收其他参数:

import Vue from 'vue'
import Vue_x from "vuex"

Vue.use(Vue_x);

export default  new Vue_x.Store({
   state: {
     count: 20,
   },
  // 需要通过 this.$store.commit('increment', 10)
  mutations: {
     increment (state, n) {
       // 变更状态
       state.count += n
     }
  }

});
Mutation基本使用

Mutation需要遵守Vue的响应规则

既然vuex中的store中的状态是响应式的,那么当我们状态变更时,监视状态的vue组件也会更新。

这就意味着vuex中的mutation也需要与使用vue一样遵守一些注意事项:

  -- 1,最好提前在你的store中初始化好所有的所需要的属性

  -- 2,当对象需要添加属性时,你应该使用

      --  Vue.set(obj, 'newProp', 123)

      --  以新对象代替老对象  state.obj = { ...state.obj, newProp: 123}

八、axios的简单使用

基于Promise的HTTP请求客户端,可以同时在浏览器和node.js使用。

使用npm安装axios

  -- npm install axios -D

基本的配置

// main.js
import axios from "axios"

Vue.prototype.$axios = axios

// 组件中
methods: {
     init () {
        this.$axios({
             method: "get",
             url: "/user"
        })
    },
};
axios的基本配置

基本的使用

test(){
          this.$axios.get(this.$store.state.apiList.course,{
            params: {
              id: 123,
            }
          }).then(function (response) {
            // 请求成功回调函数
          }).catch(function (response) {
            // 请求失败的回调函数
          })
}
get请求
test(){
          this.$axios.post(this.$store.state.apiList.course,{
              course_title: "Python",
              course_price: "19.88"
          }).then(function (response) {
            // 请求成功回调函数
          }).catch(function (response) {
            // 请求失败的回调函数
          })
}
post请求
function getCourse(){
          return this.$axios.get('/course/12')
        }
function getCourse_all() {
          return this.$axios.get('/course')
        }
this.$axios.all([getCourse_all(),getCourse()])
          .then().catch()
发送多个并发请求
methods: {
          init(){
            var that = this
            this.$axios.request({
              url: that.$store.state.apiList.course,
              method: 'get'
            }).then(function (data) {
              if (data.status === 200){
                  that.courseList = data.data
              }
            }).catch(function (reason) {
              console.log(reason)
            })
          }
},
axios.request

九、Vuex 坑!

1.setTimeout/ setInterval

  • 场景一 :this指向改变无法用this访问vue实例
  mounted(){
    setTimeout( function () {
    //setInterval同理
    console.log(this); //此时this指向Window对象
    },1000) ;
  }
  • 解决方法 :使用箭头函数或者缓存this
  //箭头函数访问this实例因为箭头函数本身没有绑定this
  setTimeout(() => {
    console. log(this);},  500) ;
    //使用变量访问this实例let self=this;
  },1000);
  setTimeout (function () {
    console. log(self);//使用self变量访问this实例
  }, 1000) ;

setInterval路由跳转继续运行并没有销毁

  • 场景一 :比如一些弹幕,走马灯文字,这类需要定时调用的,路由跳转之后,因为组件已经销毁了,但是setlnterval还没有销毁,还在继续后台调用,控制台会不断报错,如果运算量大的话,无法及时清除,会导致严重的页面卡顿。

    • 解决方法 :在组件生命周期beforeDestroy停止setInterval
  created() {
       this.intervalid = setInterval(() => {
          this.layerError = "";
          this.Timer = null;
      }, 100000);
   }
  beforeDestroy( ){
    //我通常是把setInterval( )定时器赋值给this实例,然后就可以像下面这么暂停。
    clearInterval(this.intervalid);
  }

2.Vue路由拦截浏览器后退实现草稿保存类似需求

  • 场景一 :为了防止用户突然离开,没有保存已输入的信息。

    • 解决方法 :
  //在路由组件中:mounted(){},
  beforeRouteLeave (to, from, next) {
      if(用户已经输入信息){
        //出现弹窗提醒保存草稿,或者自动后台为其保存
      }else{
        next(true);//用户离开
      }
  }

3.自定义组件添加click等事件不生效

  • 场景一 :一些自定义组件,需要额外添加一些事件来实现一些特定需求
  <template>
    <el-progress type="circle" :percentage=“0" @click="stopProgress">   </elprogress>
  </template>
  <script>
    export default {
       methods:{
            stopProgress() { 
            console.log('停止')
            }
        }
    }
  </script>
  • 解决方法:使用.native修饰符
  <template>
    <el-progress type="circle" :percentage="0" @click.native="stopProgress"></el-progress>
  </template>
  <script>
    export default {
        methods:{
            stopProgress() { 
            console.log('停止')
            }
        }
    }
  </script>

4.手动操控自定义组件

  • 场景一 :一些自定义组件,需要去获取组件对象进行一些其他的Dom操作

    • 解决方法 :使用ref属性暴露组件获取句柄
  <template>
    <el-progress type="circle" :percentage="O" ref="progress"></el-progress></template>
  <script>
    this.$refs.progress //组件对象实例, 可以手动调用组件的内置方法和属性
    this.$refs.progress.$el //组件 对象的最外层dom元素
  </script>

5.深度作用选择器

  • 场景一 : scoped的样式,希望影响到子组件的默认样式

在样式中设置完scoped在浏览器解析为如下图这样,a是个div,a div里面包含一个组件里面解析完了div的样式名字为b,想在父组件影响到子组件的默认样式。解决方法:

  <style scoped>
    .a >>> .b { /* ... */ }
  </style>
    //有些像Sass之类的预处理器无法正确解析>>>。这种情况下你可以使用/deep/操作符取而代之- - - -这是一个>>>的别名,同样可以正常工作。  <style scoped lang=“scss”>
    .a /deep/ .b { /* ... */ }
  </style>

6.Vue数组/对象更新视图不更新

  • 场景一 :很多时候我们习惯于这样操作数组和对象
  data() { 
    return {
        arr: [1,2,3],
        obj:{
          a: 1,
          b: 2 
        }
    }; 
  },
 // 数组更新视图不更新
 this.arr[0] = 'OBKoro1';
 this.arr.length = 1;
 console.log(arr);// ['OBKoro1']; 
 // 数据更新,对象视图不更新     
 this.obj.c = 'OBKoro1';
 delete this.obj.a; //删除对象的属性
 console.log(obj);  // {b:2,c:'OBKoro1'}
  • 解决方法 :

    • this. $set(你要改变的数组/对象,你要改变的位置/key,你要改成什么value)
    • 数组原生方法触发视图更新( vue官网可查):
    • 整体替换数组/对象

7.Vue Filters过滤器的使用

  • 场景一 :常见的数据文本的格式化
  <!– 在双花括号中 –>
 <div>{{ message | DateFormat }}</div>    //展示正确时间
 <!– 在'v-bind'中 –>
 <div v-bind:id='rawId | formatId'></div>
 //Demo:一个日期过滤器返回yyyy- MM-ddhh:mm:ss的样式
 //引入一个提前写好日期格式化的js  import dayjs from ‘dayjs’;
 export default {
    data() {
       return {
               //时间毫秒
               message:18324798324789
           }
   },
   filters: {
         //传入进行日期格式化
     DateFormat(value) {
       return dayjs(value).format(“YYYY-MM-DD HH:mm:ss")
         }
   }
 }

8.Vue深度watch与watch立即触发回调

  • 场景一 :在watch里面监测对象里面对应的值是监测不到的,可以用如下方法。

    • 选项: deep
    • 在选项参数中指定deep:true,可以监听对象中子属性的变化。
    • 选项: immediate
    • 在选项参数中指定immediate:true,将立即以表达式的当前值触发回调,也就是默认触发一次。
  watch: {
   obj: {
       handler(val, oldVal) {
         console.log('属性变化触发这个回调;',val, oldVal);
       },
       deep: true // 监测这个对象中每一个属性的变化
   },
   step: { // 属性 //watch
      handler(val, oldVal) {
       console.log('默认触发一次', val, oldVal);
      },
      immediate: true // 默认触发一次
   }
 }</cod  e> 

参考链接:https://www.vue-js.com/topic/5be446a7fffaa30f33091cf1

原文地址:https://www.cnblogs.com/bubu99/p/11988519.html