过滤器

Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:

过滤器的种类:

  • 全局过滤器:创建 Vue 实例之前全局定义过滤器
  • 局部过滤器:在一个组件的选项中定义本地的过滤器

tip:当全局过滤器和局部过滤器重名时,会采用局部过滤器。

全局过滤器

Vue.filter('过滤器名称', function (value[,param1,...] ) {  
//逻辑代码
})

定义局部过滤器

new Vue({       
  filters: {      
     '过滤器名称': function (value[,param1,...] ) { 
         // 逻辑代码     
       } 
    }    
})

应用过滤器

{{ 表达式 |  过滤器名字}}

案例:

<body>
  <div id="myDiv">
    <p>未使用过滤器: {{birthday}}</p>
    <p>{{birthday | dataFormat}}</p>
    <p>未使用过滤器: {{message}}</p>
    <p>将钟替换为王: {{message | messageFormat}}</p>
    <p>不传参,默认使用刘: {{message | paramFormat}}</p>
    <p>传参,使用参数: {{message | paramFormat("罗")}}</p>
  </div>
  <script src="./js/vue.js"></script>
  <script src="./js/moment.js"></script>
  <script type="text/javascript">
    Vue.filter("dataFormat", (value) => {
      return moment(value).format("YYYY-MM-DD HH:mm:ss");
    });
    Vue.filter("messageFormat", (value) => {
      return value.replace('钟', "王")
    });
    const app = new Vue({
      el: "#myDiv",
      data: {
        birthday: new Date(),
        message: '钟先生要出人头地'
      },
      filters: {
        'paramFormat': (value, param = "刘") => {
          return value.replace("钟", param)
        }
      }
    })
  </script>
</body>

侦听器

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

watch 可以让我们监控一个值的变化。从而做出相应的反应。

<body>
  <div id="app">
    <input type="text" v-model='message'>
  </div>
  <script src="./js/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        message: 'Hello World',
      },
      watch: {
        message(newMessage, oldMessage) {
          console.log('新的值' + newMessage);
          console.log('旧的值' + oldMessage);
        }
      }
    })
  </script>
</body>

深度监控

如果监控的是一个对象,需要进行深度监控,才能监控到对象中属性的变化。

以前定义监控时,person 是一个函数,现在改成了对象,并且要指定两个属性:

  • deep:代表深度监控,不仅监控 person 变化,也监控 person 中属性变化
  • handler:监控处理函数
<body>
  <div id="app">
    <input type="text" v-model="person.name"><br>
    <input type="text" v-model="person.age">
    <button @click="person.age++">+</button>
    <h2>
      姓名为:{{person.name}};年龄为:{{person.age}}
    </h2>
  </div>
  <script src="./js/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        person: {}
      },
      watch: {
        person: {
          deep: true,
          handler(obj) {
            console.log("name = " + obj.name + ", age=" + obj.age);
          }
        }
      }
    })
  </script>
</body>

组件化

全局组件

在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。所以我们会把页面的不同部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。

  • 组件其实也是一个 Vue 实例,因此它在定义时也会接收:data、methods、生命周期函数等
  • 不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有 el 属性
  • 但是组件渲染需要 html 模板,所以增加了 template 属性,值就是 HTML 模板
  • 全局组件定义完毕,任何 vue 实例都可以直接在 HTML 中通过组件名称来使用组件
  • 一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝
  • 组件可以多次复用
<div id="app">
  <!--使用定义好的全局组件-->
  <counter></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
  // 定义全局组件,两个参数:1,组件名称。2,组件参数
  Vue.component("counter",{
    template:'<button v-on:click="count++">你点了我 {{ count }} 次,我记住了.</button>',
    data(){
      return {
        count:0
      }
    }
  })
  var app = new Vue({
    el:"#app"
  })
</script>

局部组件

一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着 Vue 的加载而加载。

因此,对于一些并不频繁使用的组件,我们会采用局部注册。

我们先在外部定义一个对象,结构与创建组件时传递的第二个参数一致:

const counter = {
  template:'<button v-on:click="count++">你点了我 {{ count }} 次,我记住了.</button>',
  data(){
    return {
      count: 0
    }
  }
};

在 vue 页面中使用

var app = new Vue({
  el:"#app",
  components:{
    counter: counter // 将定义的对象注册为组件
  }
})
  • components 就是当前 Vue 对象子组件集合。
  • 效果与刚才的全局注册是类似的,不同的是,这个 counter 组件只能在当前的 Vue 实例中使用

组件传值

我们定义一个子组件,并接受复杂数据:

const myList = {
  template: '<ul><li v-for="item in items" :key="item.id"> {{item.id}} : {{item.name}} </li></ul>',
  props: {
    items: {
      type: Array,
      default: [],
      required: true
    }
  }
};

这个子组件可以对 items 进行迭代,并输出到页面。

props:定义需要从父组件中接收的属性

items:是要接收的属性名称

  • type:限定父组件传递来的必须是数组
  • default:默认值
  • required:是否必须
<div id="app">
  <!-- 使用子组件的同时,传递属性,这里使用了v-bind,指向了父组件自己的属性lessons -->
  <my-list :items="lessons"/>
</div>
<script>
  var app = new Vue({
    el: "#app",
    components:{
      myList 
    },
    data: {
      lessons:[
        {id:1, name: 'java'},
        {id:2, name: 'python'},
        {id:3, name: 'ui'},
      ]
    }
  })
</script>

另外看这个案例:

<div id="app">
    <h2>num: {{num}} </h2>
    <!--使用子组件的时候,传递num到子组件中-->
    <counter :num="num"></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
  // 子组件,定义了两个按钮,点击数字num会加或减
  Vue.component("counter", {
      template:'\
            <div>\
                <button @click="num++">加</button>  \
                <button @click="num--">减</button>  \
            </div>',
      props:['num']// count是从父组件获取的。
  })
  var app = new Vue({
    el:"#app",
    data: {
      num: 0
    }
  })
</script>

h2 中的 num 是修改不了的,为什么呢?子组件接收到父组件属性后,默认是不允许修改的。在官方文档也解释到了。https://cn.vuejs.org/v2/guide/components-props.html

只有父组件能修改,那么加和减的操作一定是放在父组件:

var app = new Vue({
  el:"#app",
  data:{
    num: 0
  },
  methods:{ // 父组件中定义操作num的方法
    increment(){
      this.num++;
    },
    decrement(){
      this.num--;
    }
  }
})

我们可以通过v-on指令将父组件的函数绑定到子组件上:

<div id="app">
  <h2>num: {{num}}</h2>
  <counter :count="num" @inc="increment" @dec="decrement"></counter>
</div>

在子组件中定义函数,函数的具体实现调用父组件的实现,并在子组件中调用这些函数。当子组件中按钮被点击时,调用绑定的函数:

Vue.component("counter", {
  template:'\
    <div>\
        <button @click="plus">加</button>  \
            <button @click="reduce">减</button>  \
        </div>',
  props:['count'],
  methods:{
    plus(){
      this.$emit("inc");
    },
    reduce(){
      this.$emit("dec");
    }
  }
})

也就是说:vue 提供了一个内置的 this.$emit() 函数,用来调用父组件绑定的函数

路由

案例使用

现在我们来实现这样一个功能:一个页面,包含登录和注册,点击不同按钮,实现登录和注册页切换。

首先我们需要先创建两个组件,分别是登录和注册

login.js

const loginForm = {
  template:'\
  <div>\
  <h2>登录页</h2> \
  用户名:<input type="text"><br/>\
  密码:<input type="password"><br/>\
  </div>\
  '
}

register.js

const registerForm = {
  template:'\
  <div>\
  <h2>注册页</h2> \
  用&ensp;户&ensp;名:<input type="text"><br/>\
  密&emsp;&emsp;码:<input type="password"><br/>\
  确认密码:<input type="password"><br/>\
  </div>\
  '
}

在首页中分别引入它们

index.html

<body>
  <div id="app" style="text-align: center;font-size: 120%;">
    <!--router-link来指定跳转的路径-->
    <span>
      <router-link to="/login">登录</router-link>
    </span>
    <span>
      <router-link to="/register">注册</router-link>
    </span>
    <hr />
    <div>
      <!--vue-router的锚点-->
      <router-view></router-view>
    </div>
  </div>
  <script src="../js/vue.js"></script>
  <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
  <script src="./login.js"></script>
  <script src="./register.js"></script>
  <script type="text/javascript">
    // 定义路由
    const routes = [{
        path: '/login',
        component: loginForm
      },
      {
        path: '/register',
        component: registerForm
      }
    ]
    // 创建 router 实例,然后传 routes 配置
    const router = new VueRouter({
      routes // (缩写) 相当于 routes: routes
    })
    var vm = new Vue({
      el: "#app",
      router
    })
  </script>
</body>
  • 通过 router-link 指定一个跳转链接,当点击时,会触发 vue-router 的路由功能。
  • 通过 router-view 来指定一个锚点,当路由的路径匹配时,vue-router 会自动把对应组件放到锚点位置进行渲染。
  • 创建 VueRouter 对象,并指定路由参数
  • routes:路由规则的数组,可以指定多个对象,每个对象是一条路由规则,包含以下属性:
    • path:路由的路径
    • component:组件名称

动态路由

经常的,像 /user/foo/user/bar/user/100/user/101 映射到同个组件上,就需要用到动态路由配置。简单的,我们可以用 :

一个 “路径参数” 使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。

routes: [
  // 动态路径参数 以冒号开头
  { path: '/user/:id', component: User }
]
<div>{{ $route.params.id }}</div>
模式匹配路径$route.params
/user/:username/user/evan{ username: 'evan' }
/user/:username/post/:post_id/user/evan/post/123{ username: 'evan', post_id: '123' }

除了 $route.params 外,$route 对象还提供了其它有用的信息,例如,$route.query (如果 URL 中有查询参数)、$route.hash 等等。

嵌套路由

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
       }
     ]
    }
  ]
})

命名路由

你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。

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

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

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

这跟代码调用 router.push() 是一回事,具体后面介绍。

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

编程式路由

router.push()

除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。

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

在 Vue 实例内部,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push

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

// 字符串
router.push('home')

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

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

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

如果提供了 pathparams 会被忽略。

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.replace()

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

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

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)

状态管理

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式

import Vue from 'vue'
import Vuex from 'vuex'

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

为了在 Vue 组件中访问 this.$store property,你需要为 Vue 实例提供创建好的 store。Vuex 提供了一个从根组件向所有子组件,以 store 选项的方式“注入”该 store 的机制:

new Vue({
  el: '#app',
  store,
  methods: {
    increment() {
      this.$store.commit('increment')
      console.log(this.$store.state.count)
    }
  }
})

注意:我们通过提交 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化。

参考资料

https://router.vuejs.org/zh/

https://vuex.vuejs.org/zh/

https://mp.weixin.qq.com/s/KrkEpUjWoQ35ZjC0CP8slQ


Last modification:June 11, 2021
如果觉得我的文章对你有用,请随意赞赏