基本示例
<!-- Counter.vue: 定义 counter 组件-->
<template>
<button @click="inc">我被点击了 {{ count }} 次</button>
</template>
<script>
export default {
data() { return { count: 0 }},
methods: {
inc() { this.count++; }
}
}
</script>
复制代码
<!-- App.vue -->
<template>
<div>
<counter style="margin-right: 5px;"/>
<counter />
</div>
</template>
<script>
import Counter from './Counter';
export defaults {
components: { Counter }
}
</script>
复制代码
- 可以复用,同一组件的不同实例互不影响
- 增强语义:
<div id="counter">
VS<counter >
多组件开发基础
- 每个组件一个文件,以后缀
.vue
结尾,一般放在src\components
目录下 - 组件文件名一般即为组件的名字,使用驼峰式命名规则,如
Counter.vue
,ItemCounter.vue
父组件,子组件
- 假如有两个组件 A, B. 在
B
组件中使用了A
组件,两个文件内容 - 组件 B 称为 组件 A 的父组件
- 父组件需要引入子组件模块:
import AComponent from './A'
- 父组件需要声明
components
属性,components: { AComponent, CComponent }
- 父组件需要引入子组件模块:
- 组件 A 称为 组件 B 的子组件
<!-- A.vue -->
<template>
<h1>Component A</h1>
</template>
<!-- B.vue -->
<template>
<div>
<a-component v-for="i in [1, 2]" :key="i" />
</div>
</template>
<script>
import AComponent from './A'
export default {
components: { AComponent }
}
</script>
复制代码
父组件向子组件传递属性
- 子组件根据传入的值进行不同的计算和渲染
- 大部分场景下,都需要向子组件传递属性,以达到复用的目的
- 子组件:
- 使用
props
数组接收父组件传递的属性 prop
属性与data
方法生成的属性在用法上完全相同- 模板与指令语法相同,如
v-for="item in items"
,{{ name }}
等 - 在
<script>
中使用要加this
,如this.items
- 模板与指令语法相同,如
- 使用
- 父组件:
- 准备往子组件传递的属性值,如
:items="user.items"
,:name="user.name"
- 准备往子组件传递的属性值,如
<!-- 子组件:Child.vue -->
<template>
<div>
<h3>{{ name }} 共有 {{ count }} 条待办事项</h3>
<ul>
<li v-for="item in items" :key="item.id"> {{ item.text }} </li>
</ul>
</div>
</template>
<script>
export default {
props: ['name', 'items'],
computed: {
count() {
return this.items.length
}
}
}
</script>
<!-- 父组件:Parent.vue -->
<template>
<div>
<child :items="user.items" :name="user.name" />
</div>
</template>
<script>
import Child from './Child'
export default {
components: { Child },
data() {
return {
user: {
name: 'zhangsan',
items: [
{id: 1, text: '好好学习'},
{id: 2, text: "天天向上" }
]
}
}
}
}
</script>
复制代码
路由:Vue-Router
- 没有路由之前:通过组合组件来组成应用
- 页面地址始终为 index.html,无法显示与内容有关的具体路径
<template>
<div>
<div id="navbar">
<button @click="select('about')">关于我</button>
<button @click="select('learn')">旅游日记</button>
</div>
<div>
<about v-if="selected === 'about'" />
<travel v-if="selected === 'travel'" />
</div>
</div>
</template>
<script>
export default {
data() { return { selected: 'about' }},
methods: {
select(value) { this.selected === value; }
}
}
</script>
复制代码
- 有了组件之后:将组件映射到路由,然后告诉 Vue Router 在哪里渲染这些组件
- 安装 vue-router 库,在工程根目录下执行
yarn add vue-router
或者npm install --Save vue-router
- 安装 vue-router 库,在工程根目录下执行
<template>
<div>
<div id="navbar">
<!-- 使用 router-link 组件来导航 -->
<!-- 使用 to 属性指定去链接 -->
<router-link to="/about">关于我</router-link>
<router-link to="/travel">旅游日记</router-link>
</div>
<div>
<!-- 路由出口 -->
<!-- 路由匹配的组件将渲染到这里 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
import About from './components/about'
import Travel from './components/travel'
import VueRouter from './vue-router'
const routes = [
{ path: '/about', component: About },
{ path: '/travel', component: Travel }
]
const router = new VueRouter({ routes })
</script>
复制代码
HTML5 History模式
-
vue-router
默认使用 hash 模式,使用 URL 的 hash 模拟一个完整的 URL- hash:表示网页中的一个位置,设置方法为
<div id="print">...</div>
<a href="">定位到 print 位置</a>
- 当点击锚点时,不会向服务器发送任何请求
- 当 URL 改变时,页面不会重新加载
- hash:表示网页中的一个位置,设置方法为
-
可以用 history 模式 替代 hash 模式
const router = new VueRouter({ mode: 'history', routes: [...] }) 复制代码
- history 模式下的 URL 更像正常的 url
状态管理:Vuex
- Vuex 是一个专为 Vue.js 应用开发的状态管理模式
- 采用集中式存储管理应用所有组件的状态
状态管理模式
-
先看一个简单的计数应用
<!-- view --> <template> <div>{{ count }}</div> </template> <script> export default { // state data() { return { count: 0}; }, // actions methods: { inc() { this.count++; } } } </script> 复制代码
-
Vue 应用一般包括
- state:驱动应用的数据
- view:以声明的方式将 state 映射到视图
- actions:响应在 view 上的用户输入导致的状态变化
-
对于简单的应用【单组件应用及简单的多组件应用】,数据流很好管理
- 但是,当应用遇到 多个组件共享同一状态 时
- 多个视图依赖于同一状态
- 参数会经过多层传递才能到达底层组件
- 对于兄弟组件之间的状态无能为力
- 来自不同视图的行为需要变更同一状态
- 可以通过事件来变更与同步状态,但维护性差
- 多个视图依赖于同一状态
- 所以,可以考虑把不同组件共享的状态抽取出来,以一个全局单例的方式进行管理
- 组件树是一个巨大的视图
- 组件树上的任何一个节点都可以读取状态并触发行为
- 适用情形
- 大型单页应用
- 对于简单的应用,可以考虑 store 模式
Store 模式
- 把所有共享的状态都放在 state 对象中
- 所有的共享状态的操作都放在 store 对象中
var store = {
debug: true,
state: {
message: 'Hello!'
},
setMessageAction (newValue) {
if (this.debug) console.log('setMessageAction triggered with', newValue)
this.state.message = newValue
},
clearMessageAction () {
if (this.debug) console.log('clearMessageAction triggered')
this.state.message = ''
}
}
复制代码
- 组件可以使用共享状态,也可以有自己的私有状态
- 组件不能直接修改共享状态,而应执行 actions 来分发事件通知 store 去改变
*Vuex 核心概念:State
-
单一状态树:每个应用仅包含唯一一个 store 实例
- 保持响应式:一旦 store 中的状态发生变化,将更新所有组件的 DOM
-
在 Vue 组件中获得 Vuex 状态
-
使用计算属性
- 问题:每个组件都直接依赖了全局状态
const Counter = { template: `<div>{{ count}}</div>`, computed: { count() { return store.state.count; } } } 复制代码
-
从根组件向子组件注入状态
- 从根组件注册 store:
const app = new Vue({ store })
- 子组件中通过
this.$store
进行访问 - 问题:当一个组件需要多个状态时,这些计算属性会显得重复和冗余
const Counter = { template: `<div>{{ count}}</div>`, computed: { count() { return this.$store.state.count; } } } 复制代码
- 从根组件注册 store:
-
使用
mapState
函数import { mapState } from 'vuex' const Counter = { template: `<div>{{ count}}</div>`, computed: mapState({ count: state => state.count }) } 复制代码
- 如果计算属性的名称与 state 的属性名相同
const Counter = { template: `<div>{{ count}}</div>`, computed: mapState([ // 映射 this.count 到 store.state.count 'count' ]) } 复制代码
- mapState 函数返回的是一个对象,如何与局部计算属性混合使用呢
- 使用扩展运算符
const Counter = { template: `<div>{{ count}}</div>`, computed: { ...mapState([ // 映射 this.count 到 store.state.count 'count' ]), localComputed() { ... } } } 复制代码
-
Vuex 核心概念:Getter
-
从 store 的 state 中派生出一些状态
computed: { doneTodosCount() { return this.$store.state.todos.filter(todo => todo.done).length; } } 复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END