这是我参与更文挑战的第4天,活动详情查看:更文挑战
过滤器
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 是修改不了的,为什么呢?子组件接收到父组件属性后,默认是不允许修改的。在官方文档也解释到了。cn.vuejs.org/v2/guide/co…
只有父组件能修改,那么加和减的操作一定是放在父组件:
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> \
用 户 名:<input type="text"><br/>\
密  码:<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-link
的 to
属性传一个对象:
<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' }})
复制代码
如果提供了 path
,params
会被忽略。
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
,是因为我们想要更明确地追踪到状态的变化。
参考资料