VueJs(2)---操作指南

VueJs(9)---组件(父子通讯)

组件(父子通讯)

 

一、概括

在一个组件内定义另一个组件,称之为父子组件。

   但是要注意的是:1.子组件只能在父组件内部使用(写在父组件tempalte中);

                                2.默认情况下,子组件无法访问父组件上的数据,每个组件实例的作用域是独立的;

那如何完成父子如何完成通讯,简单一句话:props down, events up :父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送

父传子:Props
子传父:子:$emit(eventName) 父$on(eventName)
父访问子:ref

下面对三个进行案例讲解:

 

二、父传子:Props

     组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。要让子组件使用父组件的数据,需要通过子组件的 props 选项

  使用Prop传递数据包括静态和动态两种形式,下面先介绍静态props

 

1、静态props

复制代码
<script src="https://unpkg.com/vue"></script>
<div id="example">
    <parent></parent>
</div>

<script>
    //要想子组件能够获取父组件的,那么在子组件必须申明:props
    var childNode = {
        template: '<div>{{message}}</div>',
        props: ['message']
    }

    //这里的message要和上面props中值一致
    var parentNode = {
        template: `
          <div class="parent">
            <child message="我是"></child>
            <child message="徐小小"></child>
          </div>`,
        components: {
            'child': childNode
        }
    };
    // 创建根实例
    new Vue({
        el: '#example',
        components: {
            'parent': parentNode
        }
    })
</script>
复制代码

效果:

 命名约定:

     对于props声明的属性来说,在父级HTML模板中,属性名需要使用中划线写法

    子级props属性声明时,使用小驼峰或者中划线写法都可以;而子级模板使用从父级传来的变量时,需要使用对应的小驼峰写法

上面这句话什么意思呢?

复制代码
<script>
    //这里需要注意的是props可以写成['my-message']或者['myMessage']都是可以的
    //但是template里的属性名,只能是驼峰式{{myMessage}},如果也写成{{my-message}}那么是无效的
    var childNode = {
        template: '<div>{{myMessage}}</div>',
        props: ['myMessage']
    }

    //这里的属性名为my-message
    var parentNode = {
        template: `
          <div class="parent">
            <child my-message="我是"></child>
            <child my-message="徐小小"></child>
          </div>`,
        components: {
            'child': childNode
        }
    };
   
</script>
复制代码

       如果我们childNode中的myMessage改成{{my-message}}看运行结果:

 

2.动态props

    在模板中,要动态地绑定父组件的数据到子模板的 props,与绑定到任何普通的HTML特性相类似,就是用 v-bind。每当父组件的数据变化时,该变化也会传导给子组件

复制代码
 var childNode = {
        template: '<div>{{myMessage}}</div>',
        props: ['my-message']
                }

 var parentNode = {
        template: `
  <div class="parent">
    <child :my-message="data1"></child>
    <child :my-message="data2"></child>
  </div>`,
        components: {
            'child': childNode
        },
        data() {
            return {
                'data1': '111',
                'data2': '222'
            }
        }
    };
复制代码

  

3、传递数字

初学者常犯的一个错误是使用字面量语法传递数值

复制代码
<script src="https://unpkg.com/vue"></script>
<div id="example">
    <parent></parent>
</div>

<script>
    var childNode = {
        template: '<div>{{myMessage}}的类型是{{type}}</div>',
        props: ['myMessage'],
        computed: {
            type() {
                return typeof this.myMessage
            }
        }
    }
    var parentNode = {
        template: `
  <div class="parent">
    <my-child my-message="1"></my-child>
  </div>`,
        components: {
            'myChild': childNode
        }
    };
    // 创建根实例
    new Vue({
        el: '#example',
        components: {
            'parent': parentNode
        }
    })
</script>
复制代码

结果:

     因为它是一个字面 prop,它的值是字符串 "1" 而不是 number。如果想传递一个实际的 number,需要使用 v-bind,从而让它的值被当作JS表达式计算 

     如何把String转成number呢,其实只要改一个地方。

复制代码
  var parentNode = {
        template: `
  <div class="parent">
  //只要把父组件my-message="1"改成:my-message="1"结果就变成number类型
    <my-child :my-message="1"></my-child>
  </div>`,
     
    };
复制代码

     当然你如果想通过v-bind想传一个string类型,那该怎么做呢? 

    我们可以使用动态props,在data属性中设置对应的数字1

复制代码
  var parentNode = {
  template: `
  <div class="parent">
    <my-child :my-message="data"></my-child>
  </div>`,
  components: {
    'myChild': childNode
  },
  //这里'data': 1代表就是number类型,'data': "1"那就代表String类型
  data(){
    return {
      'data': 1
    }
  }
};
复制代码

 

三、子转父 :$emit

 关于$emit的用法

   1、父组件可以使用 props 把数据传给子组件。
   2、子组件可以使用 $emit 触发父组件的自定义事件。

子主键

复制代码
<template>  
  <div class="train-city">  
    <span @click='select(`大连`)'>大连</span>  
  </div>  
</template>  
<script>  
export default {  
  name:'trainCity',  
  methods:{  
    select(val) {  
      let data = {  
        cityname: val  
      };  
      this.$emit('showCityName',data);//select事件触发后,自动触发showCityName事件  
    }  
  }  
}  
</script>  
复制代码

父组件

复制代码
<template>  
    <trainCity @showCityName="updateCity" :index="goOrtoCity"></trainCity> //监听子组件的showCityName事件。  
<template>  
<script>  
export default {  
  name:'index',  
  data () {  
   return {  
      toCity:"北京"  
    }  
  }  
  methods:{  
    updateCity(data){//触发子组件城市选择-选择城市的事件    
      this.toCity = data.cityname;//改变了父组件的值  
      console.log('toCity:'+this.toCity)        
    }  
  }  
}  
</script>  
复制代码

结果为:toCity: 大连

第二个案例

复制代码
<script src="https://unpkg.com/vue"></script>

    <div id="counter-event-example">
        <p>{{ total }}</p>
        <button-counter v-on:increment1="incrementTotal"></button-counter>
        <button-counter v-on:increment2="incrementTotal"></button-counter>
    </div>


<script>
    Vue.component('button-counter', {
        template: '<button v-on:click="increment">{{ counter }}</button>',
        //组件数据就是需要函数式,这样的目的就是让每个button-counter不共享一个counter
        data: function() {
            return {
                counter: 0
            } 
        },
        methods: {
            increment: function() {
//这里+1只对button的值加1,如果要父组件加一,那么就需要$emit事件 this.counter += 1; this.$emit('increment1', [12, 'kkk']); } } }); new Vue({ el: '#counter-event-example', data: { total: 0 }, methods: { incrementTotal: function(e) { this.total += 1; console.log(e); } } }); </script>
复制代码

详细讲解:

   1:button-counter作为父主键,父主键里有个button按钮。

   2:两个button都绑定了click事件,方法里: this.$emit('increment1', [12, 'kkk']);,那么就会去调用父类v-on所监听的increment1事件。

   3:当increment1事件被监听到,那么执行incrementTotal,这个时候才会把值传到父组件中,并且调用父类的方法。

   4:这里要注意第二个button-counter所对应的v-on:'increment2,而它里面的button所对应是this.$emit('increment1', [12, 'kkk']);所以第二个button按钮是无法把值传给他的父主键的。

 示例:一个按钮点击一次那么它自身和上面都会自增1,而第二个按钮只会自己自增,并不影响上面这个。

还有就是第一个按钮每点击一次,后台就会打印一次如下:

 

 四、ref ($refs)用法

ref 有三种用法

    1.ref 加在普通的元素上,用this.ref.name 获取到的是dom元素

    2.ref 加在子组件上,用this.ref.name 获取到的是组件实例,可以使用组件的所有方法。

    3.如何利用v-for 和ref 获取一组数组或者dom 节点

 

1.ref 加在普通的元素上,用this.ref.name 获取到的是dom元素

复制代码
<script src="https://unpkg.com/vue"></script>

<div id="ref-outside-component" v-on:click="consoleRef">
    <component-father ref="outsideComponentRef">
    </component-father>
    <p>ref在外面的组件上</p>
</div>

<script>
    var refoutsidecomponentTem = {
        template: "<div class='childComp'><h5>我是子组件</h5></div>"
    };
    var refoutsidecomponent = new Vue({
        el: "#ref-outside-component",
        components: {
            "component-father": refoutsidecomponentTem
        },
        methods: {
            consoleRef: function() {
                console.log(this.); // #ref-outside-component     vue实例
                console.log(this.$refs.outsideComponentRef); // div.childComp vue实例
            }
        }
    });
</script>
复制代码

效果:当在div访问内点击一次:

 

2.ref使用在外面的元素上

复制代码
<script src="https://unpkg.com/vue"></script>

<!--ref在外面的元素上-->
<div id="ref-outside-dom" v-on:click="consoleRef">
    <component-father>
    </component-father>
    <p ref="outsideDomRef">ref在外面的元素上</p>
</div>

<script>
    var refoutsidedomTem = {
        template: "<div class='childComp'><h5>我是子组件</h5></div>"
    };
    var refoutsidedom = new Vue({
        el: "#ref-outside-dom",
        components: {
            "component-father": refoutsidedomTem
        },
        methods: {
            consoleRef: function() {
                console.log(this); // #ref-outside-dom    vue实例
                console.log(this.$refs.outsideDomRef); //   <p> ref在外面的元素上</p>
            }
        }
    });
</script>
复制代码

 效果:当在div访问内点击一次:

 

 

3.ref使用在里面的元素上---局部注册组件

复制代码
<script src="https://unpkg.com/vue"></script>

<!--ref在里面的元素上-->
<div id="ref-inside-dom">
    <component-father>
    </component-father>
    <p>ref在里面的元素上</p>
</div>

<script>
    var refinsidedomTem = {
        template: "<div class='childComp' v-on:click='consoleRef'>" +
            "<h5 ref='insideDomRef'>我是子组件</h5>" +
            "</div>",
        methods: {
            consoleRef: function() {
                console.log(this); // div.childComp   vue实例 
                console.log(this.$refs.insideDomRef); // <h5 >我是子组件</h5>
            }
        }
    };
    var refinsidedom = new Vue({
        el: "#ref-inside-dom",
        components: {
            "component-father": refinsidedomTem
        }
    });
</script>
复制代码

  效果:当在click范围内点击一次:

 

 

4.ref使用在里面的元素上---全局注册组件

复制代码
<script src="https://unpkg.com/vue"></script>

<!--ref在里面的元素上--全局注册-->
<div id="ref-inside-dom-all">
    <ref-inside-dom-quanjv></ref-inside-dom-quanjv>
</div>

<script>
    //v-on:input指当input里值发生改变触发showinsideDomRef事件
    Vue.component("ref-inside-dom-quanjv", {
        template: "<div class='insideFather'> " +
            "<input type='text' ref='insideDomRefAll' v-on:input='showinsideDomRef'>" +
            "  <p>ref在里面的元素上--全局注册 </p> " +
            "</div>",
        methods: {
            showinsideDomRef: function() {
                console.log(this); //这里的this其实还是div.insideFather
                console.log(this.$refs.insideDomRefAll); // <input  type="text">
            }
        }
    });

    var refinsidedomall = new Vue({
        el: "#ref-inside-dom-all"
    });
</script>
复制代码

效果:当我第一次输入1时,值已改变出发事件,当我第二次在输入时在触发一次事件,所以后台应该打印两次

 

VueJs(10)---vue-router(动态路由,嵌套式路由,编程式路由)

vue-router(动态路由,嵌套式路由,编程式路由)

     

       本文是基于官网学习,官网具体学习目录:vue-router

      一、安装

          基于vue-cli脚手架安装还是蛮简单的:在文件当前目录下运行:

npm install vue-router

     如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能:

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

         脚手架安装教程:  脚手架安装教程 

        其他类型安装详见官网:安装

 

   二、单页面应用

   官网介绍的已经很全面。

复制代码
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
    <h1>Hello App!</h1>
    <p>
        <!-- 使用 router-link 组件来导航. -->
        <!-- 通过传入 `to` 属性指定链接. -->
        <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
        <router-link to="/foo">Go to Foo</router-link>
        <router-link to="/bar">Go to Bar</router-link>
    </p>
    <!-- 路由出口 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    <router-view></router-view>
</div>

<script>
    // 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)

    // 1. 定义(路由)组件。
    // 可以从其他文件 import 进来
    const Foo = {
        template: '<div>foo</div>'
    }
    const Bar = {
        template: '<div>bar</div>'
    }

    // 2. 定义路由
    // 每个路由应该映射一个组件。 其中"component" 可以是
    // 通过 Vue.extend() 创建的组件构造器,
    // 或者,只是一个组件配置对象。
    // 我们晚点再讨论嵌套路由。
    const routes = [{
        path: '/foo',
        component: Foo
    }, {
        path: '/bar',
        component: Bar
    }]

    // 3. 创建 router 实例,然后传 `routes` 配置
    // 你还可以传别的配置参数, 不过先这么简单着吧。
    const router = new VueRouter({
        routes // (缩写)相当于 routes: routes
    })

    // 4. 创建和挂载根实例。
    // 记得要通过 router 配置参数注入路由,
    // 从而让整个应用都有路由功能
    const app = new Vue({
        router
    }).$mount('#app')

    // 现在,应用已经启动了!
</script>

  <!-- 要注意,当 <router-link> 对应的路由匹配成功,将自动设置 class 属性值 .router-link-active -->
<style>
    .router-link-active {
  color: red;
 }
</style>
复制代码

效果:

  这个案例,就是不同路径显示不同组件。

 

     三、动态路由匹配

    我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。比如/user/name、/user/age、/user/haha都要映射到user组件,怎么做呢?

复制代码
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
    <p>
        <router-link to="/user/foo">/user/foo</router-link>
        <router-link to="/user/bar">/user/bar</router-link>
    </p>
    <router-view></router-view>
</div>

<script>
    //一个『路径参数』使用冒号 : 标记。当匹配到一个路由时,
    //参数值会被设置到 this.$route.params,可以在每个组件内使用。于是,我们可以更新 User 的模板
    //输出当前用户的 ID
    const User = {
        template: `<div>User {{ $route.params.id }}</div>`
    }
     // 动态路径参数 以冒号开头
     //现在呢,像 /user/foo 和 /user/bar 都将映射到相同的路由。
    const router = new VueRouter({
        routes: [{
            path: '/user/:id',
            component: User
        }]
    })

    const app = new Vue({
        router
    }).$mount('#app')
</script>

<style>
    .router-link-active {
        color: red;
    }
</style>
复制代码

效果: 

  你可以在一个路由中设置多段『路径参数』,对应的值都会设置到 $route.params 中。例如:

 模式匹配路径$route.params
  /user/:username /user/evan { username: 'evan' }
  /user/:username/post/:post_id /user/evan/post/123 { username: 'evan', post_id: 123 }

 

 

 

 

 

 

      四、嵌套路由

    嵌套路由是个常见的需求,假设用户能够通过路径/home/news和/home/message访问一些内容,一个路径映射一个组件,访问这两个路径也会分别渲染两个组件。

 实现嵌套路由有两个要点:

  • 在组件内部使用<router-view>标签
  • VueRouter 的参数中使用 children 配置
<div id="app">
  <router-view></router-view>
</div>

    这里的 <router-view> 是最顶层的出口,渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 <router-view>。例如,在 User 组件的模板添加一个 <router-view>

复制代码
const User = {
  template: `
    <div class="user">
      <h2>User {{ $route.params.id }}</h2>
      <router-view></router-view>
    </div>
  `
}
复制代码

       要在嵌套的出口中渲染组件,需要在 VueRouter 的参数中使用 children 配置

复制代码
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User,
      children: [
        {
          // 当 /user/:id/profile 匹配成功,
          // UserProfile 会被渲染在 User 的 <router-view> 中
          path: 'profile',
          component: UserProfile
        },
        {
          // 当 /user/:id/posts 匹配成功
          // UserPosts 会被渲染在 User 的 <router-view> 中
          path: 'posts',
          component: UserPosts
        }
      ]
    }
  ]
})
复制代码

     要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。

    此时,基于上面的配置,当你访问 /user/foo 时,User 的出口是不会渲染任何东西,这是因为没有匹配到合适的子路由。如果你想要渲染点什么,可以提供一个空的 子路由:

复制代码
const router = new VueRouter({
  routes: [
    {
      path: '/user/:id', component: User,
      children: [
        // 当 /user/:id 匹配成功,
        // UserHome 会被渲染在 User 的 <router-view> 中
        { path: '', component: UserHome },

        // ...其他子路由
      ]
    }
  ]
})
复制代码

 综合小案例:

复制代码
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
    <p>
        <router-link to="/user/foo">/user/foo</router-link>
        <router-link to="/user/foo/profile">/user/foo/profile</router-link>
        <router-link to="/user/foo/posts">/user/foo/posts</router-link>
    </p>
    <router-view></router-view>
</div>
<script>
    const User = {
        template: `
      <div class="user">
      <h2>User {{ $route.params.id }}</h2>
      <router-view></router-view>
      </div> `
    }
    const UserHome = { template: '<div>Home</div>' }     
    const UserProfile = {   template: '<div>Profile</div>'  }    
    const UserPosts = {   template: '<div>Posts</div>'  }
       
    const router = new VueRouter({
        routes: [{
            path: '/user/:id',
            component: User,
            children: [{
                 // /user/:id/匹配到UserHome组件,如果你是/user/:id/home是匹配不到任何组件的,因为children里没有home路径
                    path: '',
                    component: UserHome
                },

                { // /user/:id/profile匹配到UserProfile组件
                    path: 'profile',
                    component: UserProfile
                },

                { // /user/:id/posts匹配到UserPosts组件
                    path: 'posts',
                    component: UserPosts
                }
            ]
        }]
    })
    const app = new Vue({
        router
    }).$mount('#app')
</Script>
复制代码

效果:

 

五、编程式路由

   1、router.push

       上面几种路由都是直接在页面点击进行跳转,但在我们实际开发过程中,有很多按钮在执行跳转之前,还会执行一系列方法,这时可以使用 this.$router.push(location) 来修改 url,完成跳转

 想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。

 当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)

声明式编程式
<router-link :to="..."> router.push(...)

 

 

 

该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:

复制代码
// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
复制代码

 

注意:如果提供了 pathparams 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path

const userId = 123
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user

  同样的规则也适用于 router-link 组件的 to 属性。

  2.router.replace

  跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。(下面会具体说明)

声明式编程式
<router-link :to="..." replace> router.replace(...)

 

 

 

3、router.go(n)

这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)

复制代码
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)

// 后退一步记录,等同于 history.back()
router.go(-1)

// 前进 3 步记录
router.go(3)

// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
复制代码

 

4、三个比较说明:

1.this.$router.push():

      跳转到不同的url,但这个方法回向history栈添加一个记录,点击后退会返回到上一个页面。

2.this.$router.replace()

      同样是跳转到指定的url,但是这个方法不会向history里面添加新的记录,点击返回,会跳转到上上一个页面。上一个记录是不存在的。

3.this.$router.go(n)

     相对于当前页面向前或向后跳转多少个页面,类似 window.history.go(n)。n可为正数可为负数。正数返回上一个页面

4、在什么情况用push,或者在上面情况用replace,个人理解:

     一般情况下,要做前进后退的浏览记录管理的,基本上都是用router.push(),但是也是有一些特殊情况需要用到router.replace()。比如,有一个授权页,用户在按流程操作时,某一步需要授权,是直接跳到授权页,授权页提交授权请求,直到成功授权后,跳到流程中的下一步操作的地址。此处,授权请求的那页面应该用replace去替换掉自身的访问记录,防止用户跳到下一步流程后按后退键回退到授权页,而导致重复授权,简单来讲就是你跳过来了,就不让你后退又到上一个页面。

举例:

复制代码
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
    <button type="button" @click="fooClick">/user/foo</button>
    <button type="button" @click="profileClick">/user/foo/profile</button>
    <button type="button" @click="postsClick">/user/foo/posts</button>

    <router-view></router-view>
</div>
<script>
    //定义了三个组件
    const UserHome = {   template: '<div>Home</div>'  }  
    const UserProfile = {  template: '<div>Profile</div>'}  
    const UserPosts = {   template: '<div>Posts</div>'  }

    //匹配了三个路由  
    const router = new VueRouter({
        routes: [{
            path: '/user/foo',
            component: UserHome,
        }, {
            path: '/user/foo/profile',
            component: UserProfile,
        }, {
            path: '/user/foo/posts',
            component: UserPosts,
        }]
    })
    const app = new Vue({
        el: '#app',
        router,
        data: {
            foo: '/user/foo',
            profile: '/user/foo/profile',
            posts: '/user/foo/posts'
        },
        methods: {

            fooClick: function(event) {
                this.$router.push({
                    path: this.foo
                });
            },
            profileClick: function(event) {
                this.$router.push({
                    path: this.profile
                });
            },
            postsClick: function(event) {
                this.$router.push({
                    path: this.posts
                });
            }
        }
    })
</Script>
复制代码

页面:

测试结果:当使用 this.$router.push,时,前进一步后退一步都会退到上一次进入的页面,而使用 this.$router.replace并不不会调到上一个页面。

 

VueJs(11)---vue-router(命名路由,命名视图,重定向别名,路由组件传参)

vue-router

    

      上篇文章讲了第一篇vue-router相关文章,文章地址:VueJs(10)---vue-router(进阶1)

     一、命名路由

       有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。我个人理解就相当于给路径取个名字,调用的时候,这个名字就指这个路径,不然有些路径很长,直接写太麻烦。

复制代码
const router = new VueRouter({
  routes: [
    {
      path: '/user/:userId',
      name: 'user',
      component: User
    }
  ]
})
复制代码

    要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象:

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

   这跟代码调用 router.push() 是一回事:

router.push({ name: 'user', params: { userId: 123 }})

   这两种方式都会把路由导航到 /user/123 路径。

 

      二、命名视图

     1、命名视图

        命名视图只需两步:第一在router-view添加name属性,第二在路由中用components。

       有时候想同时(同级)展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar(侧导航) 和 main(主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default

<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>

一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置(带上 s)

复制代码
const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        default: Foo,
        a: Bar,
        b: Baz
      }
    }
  ]
})
复制代码

举例

复制代码
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
    <h1>Named Views</h1>
    <ul>
        <li>
            <router-link to="/">/</router-link>
        </li>
        <li>
            <router-link to="/other">/other</router-link>
        </li>
    </ul>
    <router-view class="view one"></router-view>
    <router-view class="view two" name="a"></router-view>
    <router-view class="view three" name="b"></router-view>
</div>
<script>
    const Foo = {
        template: '<div>foo</div>'
    }
    const Bar = {
        template: '<div>bar</div>'
    }
    const Baz = {
        template: '<div>baz</div>'
    }

    const router = new VueRouter({
        mode: 'history',
        routes: [{
            path: '/',
            components: {
                default: Foo,
                a: Bar,
                b: Baz
            }
        }, {
            path: '/other',
            components: {
                default: Baz,
                a: Bar,
                b: Foo
            }
        }]
    })

    new Vue({
        router,
        el: '#app'
    })
</script>
复制代码

 效果:

 

2、嵌套命名视图

    我们也有可能使用命名视图创建嵌套视图的复杂布局。这时你也需要命名用到的嵌套 router-view 组件。我们以一个设置面板为例:

  

  • Nav 只是一个常规组件。
  • UserSettings 是一个视图组件。
  • UserEmailsSubscriptionsUserProfileUserProfilePreview 是嵌套的视图组件。

 UserSettings 组件的 <template> 部分应该是类似下面的这段代码:

复制代码
<!-- UserSettings.vue -->
<div>
  <h1>User Settings</h1>
  <NavBar/>
  <router-view/>
  <router-view name="helper"/>
</div>
复制代码

   然后你可以用这个路由配置完成该布局:

复制代码
{
  path: '/settings',
  // 你也可以在顶级路由就配置命名视图
  component: UserSettings,
  children: [{
    path: 'emails',
    component: UserEmailsSubscriptions
  }, {
    path: 'profile',
    components: {
      default: UserProfile,
      helper: UserProfilePreview
    }
  }]
}
复制代码

 

 三、重定向和别名

 1、重定向

   (1)重定向也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
  ]
})

  (2) 重定向的目标也可以是一个命名的路由:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: { name: 'foo' }}
  ]
})

   (3)甚至是一个方法,动态返回重定向目标:

复制代码
const router = new VueRouter({
  routes: [
    { path: '/a', redirect: to => {
      // 方法接收 目标路由 作为参数
      // return 重定向的 字符串路径/路径对象
    }}
  ]
})
复制代码

   注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在下面这个例子中,为 /a 路由添加一个 beforeEach 或 beforeLeave 守卫并不会有任何效果。

  

2、别名

重定向和别名的区别

重定向:当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b,那么『别名』又是什么呢?

 别名:/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a一样。

上面对应的路由配置为:

const router = new VueRouter({
  routes: [
    { path: '/a', component: A, alias: '/b' }
  ]
})

 『别名』的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。

 

四、路由组件传参

   路由组件传参一样需要:props,属性,通过props我们不用在组件中用{{ $route.params.id }}获取属性值,而可以直接把route.params 设置为组件属性。

   使用 props 将组件和路由解耦:

   (1) 不用props

复制代码
const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User }
  ]
})
复制代码

    (2)通过 props 解耦

复制代码
const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User, props: true },

    // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
    {
      path: '/user/:id',
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }
  ]
})
复制代码

  这样你便可以在任何地方使用该组件,使得该组件更易于重用和测试。

  如果 props 被设置为 trueroute.params 将会被设置为组件属性。

(3)如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。

const router = new VueRouter({
  routes: [
    { path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }
  ]
})

(4)函数模式

  你可以创建一个函数返回 props。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。

const router = new VueRouter({
  routes: [
    { path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
  ]
})

    URL /search?q=vue 会将 {query: 'vue'} 作为属性传递给 SearchUser 组件。

  请尽可能保持 props 函数为无状态的,因为它只会在路由发生变化时起作用。如果你需要状态来定义 props,请使用包装组件,这样 Vue 才可以对状态变化做出反应。

 

五、HTML5 History 模式

 vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。

  如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

         当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!

 不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

 所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

VueJs(12)---vue-router(导航守卫,路由元信息,获取数据)

vue-router(导航守卫,路由元信息,获取数据)

 

        之前泄露两篇有关vue-router博客:

       VueJs(10)---vue-router(进阶1)

       VueJs(11)---vue-router(进阶2)

一、导航守卫

        当做Vue-cli项目的时候感觉在路由跳转前做一些验证,比如登录验证,是网站中的普遍需求,这个时候就需要导航守卫,你可以在页面跳转前做逻辑判断,时候跳转,跳转到指定页面。

       (1)全局的(beforeEach,afterEach,beforeResolve),

       (2)单个路由独享的(beforeEnter),

       (3)组件级的(beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave)。

 

1、全局守卫

   你可以使用 router.beforeEach 注册一个全局前置守卫:

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // 业务逻辑处
})

   每个守卫方法接收三个参数:

  • to: Route: 即将要进入的目标 路由对象

  • from: Route: 当前导航正要离开的路由

  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

   next(): 进行管道中的下一个钩子。这个是必须要的,否在无法完成跳转。

   next(false): 中断当前的导航。就相当于点击之后不会跳转到指定页面,就相当于没有写next()一样。

   next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,

   next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

     确保要调用 next 方法,否则钩子就不会被 resolved。

全局后置钩子

    你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身

router.afterEach((to, from) => {
  // ...
})

路由独享的守卫

   你可以在路由配置上直接定义 beforeEnter 守卫

复制代码
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})
复制代码

 

2、组件内的守卫

最后,你可以在路由组件内直接定义以下路由导航守卫

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave
复制代码
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}
复制代码

   beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。

     不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

   注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave来说,this 已经可用了,所以不支持传递回调,因为没有必要了。

beforeRouteUpdate (to, from, next) {
  // just use `this`
  this.name = to.params.name
  next()
}

  这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。

复制代码
beforeRouteLeave (to, from , next) {
  const answer = window.confirm('你确定要退出吗')
  if (answer) {
    next()
  } else {
    next(false)
  }
}
复制代码

下面用一个小案例说明:

复制代码
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
    <button type="button" @click="fooClick">/user/foo</button>
    <button type="button" @click="profileClick">/user/foo/profile</button>
    <button type="button" @click="postsClick">/user/foo/posts</button>

    <router-view></router-view>
</div>
<script>
    //定义了三个组件
    const UserHome = { template: '<div>Home</div>'}  
    const UserProfile = {template: '<div>Profile</div>' } 
    const UserPosts = {template: '<div>Posts</div>'}        

    //匹配了三个路由  
    const router = new VueRouter({
            routes: [{
                path: '/user/foo',
                component: UserHome,
                beforeEnter: (to, from, next) => {
                    console.log('进入UserHome组件');
                    next();
                }
            }, {
                path: '/user/foo/profile',
                component: UserProfile,
                beforeEnter: (to, from, next) => {
                    console.log('进入profile组件');
                    next();
                }
            }, {
                path: '/user/foo/posts',
                component: UserPosts,
                beforeEnter: (to, from, next) => {
                    console.log('UserPosts组件');
                    //这里设置flase说明就算路径为'/user/foo/posts',也不会进入UserPosts组件
                    next(false);
                }
            }]
        })
        //设置全局导航守卫    
    router.beforeEach((to, from, next) => {
        console.log('进入全局守卫导航');
        console.log('进入路由路径:' + to.path);
        console.log('离开路由路径:' + from.path);
        next();
    })
    const app = new Vue({
        el: '#app',
        router,
        data: {
            foo: '/user/foo',
            profile: '/user/foo/profile',
            posts: '/user/foo/posts'
        },
        methods: {
            fooClick: function(event) {
                this.$router.push({
                    path: this.foo
                });
            },
            profileClick: function(event) {
                this.$router.push({
                    path: this.profile
                });
            },
            postsClick: function(event) {
                this.$router.push({
                    path: this.posts
                });
            } }    
    })
</Script>
复制代码

效果:

通过上面可以得出以下结论:

     1、全局守卫会比组件守卫先执行

     2.全局守卫第一次加载页面to和from路径都是“/”

     3.每一次路径改变都会调用全局组件,路径不变是不会调用全局守卫,也不会在调用组件守卫

     4.我在/user/foo/posts路径设置为next(flase)是,当点击它是并没有跳转UserPosts组件,页面显示的还是profile说明跳转失败。

 

二、路由元信息(meta)

    为什么会有路由元信息这个东西?首先要知道它到底有什么作用。

   我们在做网站登录验证的时候,可以使用到beforeEach 钩子函数进行验证操作,如下面代码 ,如果页面path为’/goodsList’,那么就让它跳转到登录页面,实现了验证登录。

复制代码
router.beforeEach((to, from, next) => {
  if (to.path === '/goodsList') {
    next('/login')
  } else 
    next()
})
复制代码

 如果需要登录验证的网页多了怎么办?

1.这里是对比path。如果需要验证的网页很多,那么在if条件里得写下所有的路由地址,将会是非常麻烦的一件事情。

2.因为路由是可以嵌套的。有’/goodsList’,那么可能会有’/goodsList/online’,再或者还有’/goodsList/offline’、’/goodsList/audit’、’/goodsList/online/edit’等等。

如果像刚才例子中这样对比(to.path === ‘/goodsList’),就非常单一,其他的路径压根不会限制(验证)到,照样能正常登陆!因为每个to.path根本不一样。

我们所理想的就是把’/goodsList’限制了,其他所有的以’/goodsList’开头的那些页面都给限制到!

to Route: 即将要进入的目标 路由对象 
我们打印一下to

它有很多属性,有 
  - fullPath 
  - hash 
  - matched 
  - meta 
  - name 
  - params 
  - path 
  - query

其中有个属性,matched,就是匹配了的路由,我们打印出来,这个是个数组。它的第一项就是{path: “/goodslist”},一直到最为具体的当前path (例如:{path: “/goodslist/online/edit”})

这里可以循环matched这个数组,看每一项的path 有没有等于’/goodsList’,只要其中一个有,那么就让它跳转到登录状态

复制代码
router.beforeEach((to, from, next) => {
  if (to.matched.some(function (item) {
    return item.path == '/goodslist'
  })) {
    next('/login')
  } else 
    next()
})
复制代码

那么这里只是对goodsList进行验证判断,且限制的还是path,就相当于把goodsList下面都给限制了,但有些东西是不需要登陆直接可以显示在页面的,比如:资讯信息,广告信息。这么做不就不可行了。用path来做限制似乎有点不好用。轮到主角登场了

meta字段(元数据)

直接在路由配置的时候,给每个路由添加一个自定义的meta对象,在meta对象中可以设置一些状态,来进行一些操作。用它来做登录校验再合适不过了

复制代码
{
  path: '/actile',
  name: 'Actile',
  component: Actile,
  meta: {
    login_require: false
  },
},
{
  path: '/goodslist',
  name: 'goodslist',
  component: Goodslist,
  meta: {
    login_require: true
  },
  children:[
    {
      path: 'online',
      component: GoodslistOnline
    }
  ]
}
复制代码

这里我们只需要判断item下面的meta对象中的login_require是不是true,就可以做一些限制了

复制代码
router.beforeEach((to, from, next) => {
  if (to.matched.some(function (item) {
    return item.meta.login_require
  })) {
    next('/login')
  } else 
    next()
})
复制代码

     meta还可以放其它信息,比如可以存储该路由相关信息(例如:设置每个路由的title,取路由的title设置为选项卡的标题)

复制代码
{
      path: '/router2',
      name: 'router2',
      component:router2,
      meta:{
        title:"积分模块"
      }
    }
复制代码
复制代码
// 全局前置守卫
router.beforeEach((to,from,next) => {
    console.log(to);
    console.log(from);
    if(to.meta.title) {
      document.title = to.meta.title;
    } else {
      document.title = '我是默认的title'
    }
    next();
});
复制代码

 

三、获取数据

有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。

我们可以通过两种方式来实现:

(1)导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示『加载中』之类的指示。

(2)导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。

 

1、导航完成后获取数据

      当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。

假设我们有一个 Post 组件,需要基于 $route.params.id 获取文章数据:

复制代码
<template>
  <div class="post">
    <div class="loading" v-if="loading">
      Loading...
    </div>

    <div v-if="error" class="error">
      {{ error }}
    </div>

    <div v-if="post" class="content">
      <h2>{{ post.title }}</h2>
      <p>{{ post.body }}</p>
    </div>
  </div>
</template>
复制代码
复制代码
export default {
  data () {
    return {
      loading: false,
      post: null,
      error: null
    }
  },
  created () {
    // 组件创建完后获取数据,
    // 此时 data 已经被 observed 了
    this.fetchData()
  },
  watch: {
    // 如果路由有变化,会再次执行该方法
    '$route': 'fetchData'
  },
  methods: {
    fetchData () {
      this.error = this.post = null
      this.loading = true
      // replace getPost with your data fetching util / API wrapper
      getPost(this.$route.params.id, (err, post) => {
        this.loading = false
        if (err) {
          this.error = err.toString()
        } else {
          this.post = post
        }
      })
    }
  }
}
复制代码
(err, post) =>是ES6写法,意思大概就是function(err, post){}大括号就相当于方法类逻辑。

2、在导航完成前获取数据

通过这种方式,我们在导航转入新的路由前获取数据。我们可以在接下来的组件的 beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法。

复制代码
export default {
  data () {
    return {
      post: null,
      error: null
    }
  },
  beforeRouteEnter (to, from, next) {
    getPost(to.params.id, (err, post) => {
      next(vm => vm.setData(err, post))
    })
  },
  // 路由改变前,组件就已经渲染完了
  // 逻辑稍稍不同
  beforeRouteUpdate (to, from, next) {
    this.post = null
    getPost(to.params.id, (err, post) => {
      this.setData(err, post)
      next()
    })
  },
  methods: {
    setData (err, post) {
      if (err) {
        this.error = err.toString()
      } else {
        this.post = post
      }
    }
  }
}
复制代码

在为后面的视图获取数据时,用户会停留在当前的界面,因此建议在数据获取期间,显示一些进度条或者别的指示。如果数据获取失败,同样有必要展示一些全局的错误提醒。

VueJs(13)---过滤器

过滤器

 

过滤器

     1、过滤器规则

       Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:

      双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:

<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

     你可以在一个组件的选项中定义本地的过滤器:

复制代码
filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}
复制代码

 或者在创建 Vue 实例之前全局定义过滤器:

复制代码
Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

new Vue({
  // ...
})
复制代码

下面这个例子用到了 capitalize 过滤器:发现首字母大写

capitalize 过滤器函数将会收到 message 的值作为第一个参数。

 

2、过滤器可以串联:

{{ message | filterA | filterB }}

这就相当于:

   第一步:先把message放到filterA过滤器中进行过滤

   第二步:将第一步过滤器的结果再放到filterB再进行过滤,显示最终过滤结果。

 

3、过滤器是 JavaScript 函数,因此可以接收参数:

{{ message | filterA('arg1', arg2) }}

 这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。

小案例

复制代码
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
    <span>SearchByName: </span>
    <input v-model="searchQuery">
    <table>
        <thead>
            <tr>
                <td v-for="col in columns">{{col|capitalize}}</td>
            </tr>
        </thead>
        <tbody>
            <tr v-for="entry in filteredData">
                <td v-for="col in columns">{{entry[col]}}</td>
            </tr>
        </tbody>
    </table>
</div>

<script>
    new Vue({
        el: '#app',
        data: {
            searchQuery: '',
            columns: ['name', 'gender', 'age'],
            data: [{
                name: 'Jack',
                gender: 'male',
                age: 26
            }, {
                name: 'John',
                gender: 'female',
                age: 20
            }, {
                name: 'Lucy',
                gender: 'female',
                age: 16
            }]
        },
        filters: {
            capitalize: function(value) {
                return value.charAt(0).toUpperCase() + value.slice(1);
            }
        },
        computed: {
            filteredData: function() {
                var self = this;
//filter是Javascript带的方法,
// 前面调用的是需要使用filter的数组类型,而给filter函数传入的是数组中的每个item,也就是说filter里面的函数,是每个item要去做的,并将每个结果返回。
return this.data.filter(function(item) { return item.name.toLowerCase().indexOf(self.searchQuery.toLowerCase()) !== -1; }) } } }); </script>
复制代码

效果:

   使用js中的迭代函数filter :

其他的一些Js 迭代方法——filter()、map()、some()、every()、forEach()、lastIndexOf()、indexOf()

 

 

VueJs(14)---理解Vuex

理解Vuex

 

一、Vuex 是什么?

      首先我们来分析一种实际开发中用vue.js的场景,你有n个组件,当你改变一个组件数据的时候需要同时改变其它n个组件的数据,那么我想你可能会对 vue 组件之间的通信感到崩溃 。

  这个时候vuex作用就来了,它可以集中管理所有组件共享的数据,做到改一个而是全部组件进行同步更新。什么意思呢?

    下面用案例说明:

  1. 单纯依赖于vue.js

  2. 依赖vue.js,也使用了vuex技术

   目的是通过对比引出vuex的概念、优势和劣势。现在开始。

 假设一个微小的应用,有一个标签显示数字,两个按钮分别做数字的加一和减一的操作。用户界面看起来是这样的:

1、纯vue.js代码:

复制代码
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
  <p>{{count}}
    <button @click="inc">+</button>
    <button @click="dec">-</button>
  </p>
</div>
<script>
new Vue({
  el:'#app',
  data () {
    return {
      count: 0
    }
  },
  methods: {
    inc () {
      this.count++
    },
    dec () {
      this.count--
    }
  }
})
</script>
复制代码

 

整个代码结构非常清晰,代码是代码,数据是数据。代码就是放在methods数组内的两个函数inc、dec,被指令@click关联到button上。而data内返回一个属性count,此属性通过{{count}}绑定到标签p内。

2、依赖vue.js,也使用了vuex技术

复制代码
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
    <p>{{count}}
        <button @click="inc">+</button>
        <button @click="dec">-</button>
    </p>
</div>
<script>
    const store = new Vuex.Store({
        state: {
            count: 0
        },
        mutations: {
            inc: state => state.count++,
            dec: state => state.count--
        }
    })

    const app = new Vue({
        el: '#app',
        computed: {
            count() {
                return store.state.count
            }
        },
        methods: {
            inc() {
                store.commit('inc')
            },
            dec() {
                store.commit('dec')
            }
        }
    })
</script>
复制代码

这两个的区别在呢?

 1、methods数组还是这两个方法,这和demo1是一样的;但是方法内的计算逻辑,不再是在函数内进行,而是提交给store对象!这是一个新的对象!

  2、count数据也不再是一个data函数返回的对象的属性;而是通过计算字段来返回,并且在计算字段内的代码也不是自己算的,而是转发给store对象。再一次store对象!

就是说vuex把组件中的数据和改变数据的方法给抽离出来,进行全局管理,这个时候你改变组件某一数据的时候,改变的全局的数据,那么对于其它通过store.state.count

得到的count数据当然会一起改变咯。

Vuex 就是一个专为 Vue.js 应用程序开发的状态管理模式。这个状态自管理应用包含以下几个部分:

  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入导致的状态变化。

 

二、Store

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

复制代码
// 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})
复制代码

 现在,你可以通过 store.state 来获取状态对象,以及通过 store.commit 方法触发状态变更:

store.commit('increment')

console.log(store.state.count) // -> 1

 

VueJs(15)--- Webstorm+Chrome 调试Vue项目

Webstorm+Chrome 调试Vue项目

前言

在项目开发中,Debug模式是非常有必要的,后端对于IDEA工具而言Debug模式非常方便,但前端WebStorm而言如果启用Debug模式是需要单独去配置一些东西,

所以这里整理自己搭建成功的过程分享出来。可能不同的Webstorm版本会导致效果不一样,所以这里先列出我的版本。

Webstorm版本: 2018.3.4

一、新建Vue项目

为了演示一个完整的示例,所以从创建项目开始,这里通过 vue脚手架命令 开始创建一个项目

 vue init webpack debug-vue

如果运行成功就会创建一个名称为 debug-vue 的vue项目,我们通过 WebStorm 打开这个项目并运行,启动命令

npm run dev

启动成功后查看页面

这就代表这个项目运行成功,这里我们在代码添加一个按钮,等下我们通过点击这个按钮来查看Debug模式是否能够成功到打的断点处。

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h2>首页</h2>
    <button @click="testMethods">测试bug断点</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  methods:{
    testMethods: function () {
      alert("你点我我就跳出来")
    }
  }
}
</script>

改好之后,再来查看页面 就变成这样了

很明显可以看出,当点击这个按钮会弹出一个框,说明绑定点击事件成功。


二、WebStorm配置

1、设置调试器端口

2、 添加调试配置

这样Webstorm就配置好了。


三、测试

第一步 先在指定代码中添加一个断点,然后启动项目

第二步 启动项目,命令

npm run dev

启动成功后

第三步 启动上面配置的调试按钮

第四步 测试

很完美的断点打过来了,在展示一个完整的请求过程

 

VueJs(16)---Nuxt引入mavon-editor插件实现markdown功能

Vue引入mavon-editor插件实现markdown功能

说明 mavon-editor是一款基于Vue的markdown编辑器,因为当前项目是采用Nuxt,所以这里所展示的教程是针对Nuxt引入mavon-editor插件,如果你只是采用了

Vue,没有用Nuxt框架,那么你可以看mavon-editor官方文档,有详细说明,其实它们只有在引入mavon-editor方式有细微差别,使用都是一样的。mavonEditor官方地址

一、Nuxt引入mavon-editor插件

1、安装

通过命令安装插件

npm install mavon-editor --save

2、在plugins中创建vueMarkdown.js

import Vue from 'vue'
import mavonEditor from 'mavon-editor'
import "mavon-editor/dist/css/index.css";
Vue.use(mavonEditor)

3、在nuxt.config.js中引入

  plugins: [
    '~/plugins//vueMarkdown.js',
  ],

这三步也是Nuxt新增插件的标准3步曲了。既然插件已经添加完成,那么接下来就是使用了,使用应该包含两部分:1)编辑markdown文档并保存。2)回显markdown数据


二、使用mavon-editor编辑

1、源码

<template>
  <div class="home">
    <mavon-editor
            ref="md"
            placeholder="请输入文档内容..."
            :boxShadow="false"
            style="z-index:1;border: 1px solid #d9d9d9;height:50vh"
            v-model="content"
            :toolbars="toolbars"
          />
  </div>
</template>
 
<script>
export default {
  name: "home",
  components: {},
  data() {
    return {
      content: "",
      toolbars: {
        bold: true, // 粗体
        italic: true, // 斜体
        header: true, // 标题
        underline: true, // 下划线
        strikethrough: true, // 中划线
        mark: true, // 标记
        superscript: true, // 上角标
        subscript: true, // 下角标
        quote: true, // 引用
        ol: true, // 有序列表
        ul: true, // 无序列表
        link: true, // 链接
        imagelink: true, // 图片链接
        code: true, // code
        table: true, // 表格
        fullscreen: true, // 全屏编辑
        readmodel: true, // 沉浸式阅读
        htmlcode: true, // 展示html源码
        help: true, // 帮助
        /* 1.3.5 */
        undo: true, // 上一步
        redo: true, // 下一步
        trash: true, // 清空
        save: false, // 保存(触发events中的save事件)
        /* 1.4.2 */
        navigation: true, // 导航目录
        /* 2.1.8 */
        alignleft: true, // 左对齐
        aligncenter: true, // 居中
        alignright: true, // 右对齐
        /* 2.2.1 */
        subfield: true, // 单双栏模式
        preview: true // 预览
      }
    };
  },
  methods: {
    // 上传图片方法
    $imgAdd(pos, $file) {
      console.log(pos, $file);
    }
  }
};
</script>

2、页面展示编辑器效果

页面展示效果如下

我们可以看到我们已经可以正常使用markdown编辑器了,说明当前插件安装成功,可以用。


三、使用mavon-editor回显

上一步是已经可以编辑,但我们还需要将我们编辑好已经存在数据库的数据,回显在页面。

1、源码

          <no-ssr>
          <mavon-editor
            v-highlight
            class="md"
            :value="content"
            :subfield = "false"
            :defaultOpen = "'preview'"
            :toolbarsFlag = "false"
            :editable="false"
            :scrollStyle="true"
           ></mavon-editor>
        </no-ssr>

属性解释

:value="content":引入要转换的内容
:subfield = "false":开启单栏模式
:defaultOpen = "'preview'":默认展示预览区域
:toolbarsFlag = "false":关闭工具栏
:editable="false":不允许编辑
scrollStyle="true":开启滚动条样式(暂时仅支持chrome)

2、页面展示效果

这样一来整个编辑回显的效果这里都展示出来了。


四、代码高亮

上面展示的时候确实已经可以正常回显markdown文档,但还不美观,我们想要的就是只要是代码都能高亮的显示出来,你可以用highlight.js插件,但我在使用中并没有达到我

自己喜欢的样式,所以我这边自己修改了部分css样式。这里把css样式展示如下。


  .markdown-body .lang- {
    display: block;
    overflow: auto;
    padding: 1.3em 2em !important;
    font-size: 16px !important;
    background: #272822 !important;
    color: #FFF;
    max-height: 700px;
  }

  .markdown-body .hljs {
    display: block;
    overflow: auto;
    padding: 1.3em 2em !important;
    font-size: 16px !important;
    background: #272822 !important;
    color: #FFF;
    max-height: 700px;
  }

  .hljs,
  .hljs-tag,
  .hljs-subst {
    color: #f8f8f2 !important;
  }

  .hljs-strong,
  .hljs-emphasis {
    color: #a8a8a2 !important;
  }

  .hljs-bullet,
  .hljs-quote,
  .hljs-number,
  .hljs-regexp,
  .hljs-literal,
  .hljs-link {
    color: #ae81ff !important;
  }

  .hljs-code,
  .hljs-title,
  .hljs-section,
  .hljs-selector-class {
    color: #a6e22e !important;
  }

  .hljs-strong {
    font-weight: bold;
  }

  .hljs-emphasis {
    font-style: italic;
  }

  .hljs-keyword,
  .hljs-selector-tag,
  .hljs-name,
  .hljs-attr {
    color: #f92672 !important;
  }

  .hljs-symbol,
  .hljs-attribute {
    color: #66d9ef !important;
  }

  .hljs-params,
  .hljs-class .hljs-title {
    color: #f8f8f2 !important;
  }

  .hljs-string,
  .hljs-type,
  .hljs-built_in,
  .hljs-builtin-name,
  .hljs-selector-id,
  .hljs-selector-attr,
  .hljs-selector-pseudo,
  .hljs-addition,
  .hljs-variable,
  .hljs-template-variable {
    color: #e6db74 !important;
  }

  .hljs-comment,
  .hljs-deletion,
  .hljs-meta {
    color: #75715e !important;
  }

然后我们再来看页面展示效果

是不是明显感觉代码已经高亮,整体看去的视觉效果比上面的要好多了。

总结 这里有关编辑保存与后台接口交互的逻辑没有粘贴出来,还有如果使用markdown编辑器上传图片这里也没有说明,具体的可以看下官方文档。

 

posted @ 2022-02-22 19:09  hanease  阅读(128)  评论(0)    收藏  举报