文章目录
一、组件
1. 创建组件
生成
<template>
<div> <!-- 必须是唯一根元素 -->
<p>组件:{{ title }}-{{ num }}</p>
<div>
<p>{{ count }}</p>
<button @click="count +=1 ">按钮</button>
</div>
<button @click="sendMessageHandle">传递数据</button>
</div>
</template>
<script>export default {
data(){
return{
count:0,
message:"我是Hello的数据"
}
},
props:{
title:{
type:String,
default:"默认数据"
},
num:{
type:Number,
default:0
}
},
methods:{
sendMessageHandle(){
this.$emit("onMyEvent",this.message)
}
}
}</script>
<style>
</style>
2. 引用组件
1. 引入
import Hei from "./components/Hei";
2. 依赖注入
components: {
Hei, // 注入
}
3. 以标签的形式使用
<Hello />
组件的使用是独立的(独立被实例化)。组件的数据空间是相互隔离的。
要求data必须是一个函数,才能保证组件是独立实例化的。
data() {
return {
......
};
},
......
组件之间可以随意引用。
3. 组件之间如何传递参数
- 通过props属性,父组件向子组件传递参数
import组件的是“父”组件,被import的组件是“子”组件,而props属性定义位于子组件中。
父组件:
<Hello title="组件基础" :num="0" />
-------------------------------------------------------------
子组件:
<p>组件:{{ title }}-{{ num }}</p>
props传递参数没有类型限制,参数的个数也没有限制。
- 通过自定义事件子组件向父组件传递参数
$emit()在子组件中自定义事件,父组件通过自定义的同名事件接收子组件传递过来的参数。
子组件:
<button @click="sendMessageHandle">传递数据</button>
......
data(){
return{
count:0,
message:"我是Hello的数据"
}
},
methods:{
sendMessageHandle(){
this.$emit("onMyEvent",this.message) # message -> data
}
}
-------------------------------------------------------------
父组件:
<p>{{ helloMessage }}</p>
<Hello @onMyEvent="getHelloMessageHandle"/>
4. 插槽
插槽是组件传递数据的另外一种方式。不仅可以传递数据,还可以传递视图。插槽多用于封装组件。
4.1 基本使用
父组件:
<template>
<div id="app">
<MyComponent> <!--不是以单标签的方式引用-->
<div>MyComponent双标签内的内容就是插槽——默认不显示,只显示子组件的内容:“插槽”</div>
</MyComponent>
</div>
</template>
<script>import MyComponent from "./components/MyComponent"
export default {
name: 'App',
components: {
MyComponent
}
}</script>
----------------------------------------------------
子组件:
<template>
<div>
<h3>插槽</h3><!--“插槽”将在父组件中显示-->
<div>
<slot></slot> <!--双标签或者单标签都可以-->
<!--slot标签将被父组件的MyComponent标签中的内容所替换-->
</div>
</div>
</template>
<script>export default {
}</script>
<style>
</style>
实现效果:
父组件将
<div>MyComponent双标签内的内容就是插槽——默认不显示,只显示子组件的内容:“插槽”</div>
4.2 编译作用域
即变量写在哪里就在哪里声明。
比如msg变量:
父组件:
<template>
<div id="app">
<MyComponent>
<div>{{ msg }}</div>
</MyComponent>
</div>
</template>
<script>import MyComponent from "./components/MyComponent"
export default {
name: 'App',
data(){
return{
msg:"我是插槽内容"
}
},
components: {
MyComponent
}
}</script>
这里的变量msg写在父组件中就在父组件中声明,而不是在子组件中声明。
4.3 后备内容
父组件:
<template>
<div id="app">
<MyComponent>
<!--什么也不给-->
</MyComponent>
</div>
</template>
<script>import MyComponent from "./components/MyComponent"
export default {
name: 'App',
components: {
MyComponent
}
}</script>
----------------------------------------------------
子组件:
<template>
<div>
<h3>插槽</h3>
<div>
<slot>默认值/后备内容</slot>
</div>
</div>
</template>
<script>export default {
}</script>
<style>
</style>
父组件如果在引入的MyComponent标签中什么也不给,那么最终将显示子组件slot标签中的内容——默认值,也称后备内容。如果父组件中引入的MyComponent标签中给了内容,那么该内容将替换slot标签及其内容——覆盖显示。
4.4 具名插槽
插槽可以有多个,我们可以使用名字来区分。
父组件:
<template>
<div id="app">
<MyComponent>
<template v-slot:header>
<div>头部</div>
</template>
<template v-slot:body>
<div>内容</div>
</template>
<template v-slot:footer>
<div>底部</div>
</template>
</MyComponent>
</div>
</template>
-------------------------------------------------------------
子组件:
<template>
<div>
<h3>插槽</h3>
<div>
<slot name="header">默认值/后备内容</slot>
<hr>
<slot name='body'>默认值/后备内容</slot>
<hr>
<slot name="footer">默认值/后备内容</slot>
</div>
</div>
</template>
注意v-slot指令只能在template标签中使用。
4.5 作用域插槽
数据定义在子组件中,但不通过子组件显示,而是传递给父组件显示。
父组件:
<template>
<div id="app">
<MyComponent>
<template v-slot:default="slotProps">
<h3>{{slotProps.demo}}</h3>
</template>
</MyComponent>
</div>
</template>
<script>import MyComponent from "./components/MyComponent"
export default {
name: 'App',
components: {
MyComponent
}
}</script>
--------------------------------------------------------
子组件:
<template>
<div>
<h3>插槽</h3>
<div>
<slot :demo="data"></slot>
</div>
</div>
</template>
<script>export default {
data(){
return{
data:"测试"
}
}
}</script>
这种用法常见于子组件有数据但不知道如何显示,而父组件知道如何显示。那么子组件就把数据传递给父组件去显示。
5. 动态组件和异步组件
5.1 动态组件
<template>
<div id="app">
<div>
<Dynamic/>
</div>
</div>
</template>
<script>import Dynamic from "./components/Dynamic";
export default {
components: {
Dynamic
}
};</script>
----------------------------------------
Dynamic.vue:
<template>
<div>
<h3>动态组件</h3>
<button @click="changeHandler">切换视图</button>
<keep-alive> <!--记录用户原来的输入,待用户从其他页面返回后还能看到当初的输入。-->
<component :is="componentId"></component>
</keep-alive>
</div>
</template>
<script>import Child1 from "./Child1";
import Child2 from "./Child2";
export default {
data() {
return {
componentId: Child1,
};
},
components: {
Child1,
Child2,
},
methods: {
changeHandler() {
if (this.componentId === Child1) {
this.componentId = Child2;
} else {
this.componentId = Child1;
}
},
},
};</script>
----------------------------------------
Child1.vue:
<template>
<div>
<h3>Child1</h3>
</div>
</template>
----------------------------------------
Child2.vue:
<template>
<div>
<h3>Child2</h3>
<p>{{msg}}</p>
<button @click="clickHandler">修改</button>
</div>
</template>
<script>export default {
data() {
return {
msg:"第一次的输入"
};
},
methods: {
clickHandler(){
this.msg="用户已经输入的数据"
}
}
};</script>
5.2 异步组件
针对组件的加载方式提出:
//传统组件加载方式:
import MyComponent from "./MyComponent ";
//异步组件加载方式:
const MyComponent = () => import ("./MyComponent");
区别就在于传统方式组件在js代码加载后既被加载,而异步组件加载是一种“懒”加载方式——按需加载。异步组件加载可以将无需首次显示的组件延迟加载,以获得速度上的提升。
6. 边界处理
Vue构建的一种全局使用对象的方式。这种对象使用方式超越了组件的名字空间,可以在全局名字空间下借助$符号完成对js对象的调用。
常见的引用有:
- $root
比如,可以在src/main.js的new Vue()出来的根实例中定义data,methods
new Vue({
......,
data() {
baby:"my girl friend"
},
methods:{
dating(){
return "cradle-snatcher love"
}
}
}).$mount('#app')
在Vue实例的子组件中(所有组件都是它的子组件)引用
<p>{{$root.baby}}</p>
<p>{{$root.dating()}}</p>
- $parent
父组件中定义的对象在子组件中也可以直接引用
<p>{{$parent.objDefinedInParent}}</p>
很显然边界处理的方式容易引起强耦合,一般不建议使用。
二、路由
Vue的路由主要是应用在单页面应用SPA中。传统的页面跳转主要通过a标签打开一个全新的页面,而Vue的路由使得视图发生更换但页面不发生跳转。
Vue的路由插件Vue Router是官方维护的。
官网的参考手册:https://router.vuejs.org/zh/installation.html
1. vue-router的安装
# save参数表示将安装的版本等信息以组件依赖(dependencies)保存进package.json
npm install
如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能:
编辑src/main.js
import VueRouter from 'vue-router'
Vue.use(VueRouter)
然后就可以启动项目npm run serve
2. vue-router的使用
Home.vue:
<template>
<div>
<h3>首页</h3>
</div>
</template>
--------------------------------------------------------------
User.vue:
<template>
<div>
<h3>用户界面</h3>
</div>
</template>
import Vue from 'vue'
import App from './App.vue' //注意:App.vue是默认第一个加载的vue组件
import './registerServiceWorker'
import VueRouter from 'vue-router'
import Home from "./pages/Home.vue"
import User from "./pages/User.vue"
Vue.use(VueRouter)
Vue.config.productionTip = false
//定义路由
const routes = [
{
path:"/",
component:Home
},
{
path:"/user",
component:User
}
]
//创建路由对象
const router = new VueRouter({
routes
})
new Vue({
router, //挂载路由
render: h => h(App),
}).$mount('#app')
- 在App.vue中定义路由出口
App.vue默认是第一个加载的vue组件。
<template>
<div id="app">
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染/显示在这里 -->
<router-view></router-view>
</div>
</template>
3. 导航效果
在App.vue中添加router-link标签:
<template>
<div id="app">
<router-link to="/">首页</router-link> |
<router-link to="/user">用户</router-link>
<router-view></router-view>
</div>
</template>
4. 动态路由匹配
//定义路由
const routes = [
{
path:"/user/:userId",
component:User
}
]
<router-link to="/user/8888">用户</router-link>
<h3>用户界面:{{$route.params.userId}}</h3>
传递的路径参数有没有限制?没有。
比如传递的数量就没有限制:
const routes = [
{
path:"/user/:userId/:name/:age",
component:User
}
]
----------------------------------------------------------
<router-link to="/user/8888/beeworkshop/31">用户</router-link>
----------------------------------------------------------
<template>
<div>
<h3>用户ID:{{$route.params.userId}}</h3>
<h3>用户姓名:{{$route.params.name}}</h3>
<h3>用户年龄:{{$route.params.age}}</h3>
</div>
</template>
5. 文件分离
就是将路由的定义拆分到main.js之外的其他文件中去。具体组织如下
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from "../pages/Home.vue"
import User from "../pages/User.vue"
Vue.use(VueRouter)
//定义路由
const routes = [
{
path:"/",
component:Home
},
{
path:"/user/:userId/:name/:age",
component:User
}
]
//创建路由对象
const router = new VueRouter({
routes
})
//导出路由对象
export default
import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from "./router" //默认导入index.js
Vue.config.productionTip = false
new Vue({
router, //挂载路由
render: h => h(App),
}).$mount('#app')
<template>
<div id="app">
<!--路由跳转-->
<router-link to="/">首页</router-link> |
<router-link to="/user/8888/beeworkshop/31">用户</router-link>
<!--路由入口-->
<router-view></router-view>
</div>
</template>
6. 路由嵌套
文档结构
- 定义嵌套路由
src/router/index.js
import Bee from "../pages/Bee" //默认导入扩展名为.vue的组件
import BigBee from "../pages/bee/BigBee"
import SmallBee from "../pages/bee/SmallBee"
//定义路由
//定义 /bee/bigbee 和 /bee/smallbee
const routes = [
{
//URL的父级路径
path:"/bee",
component:Bee,
//URL的子路径(children中可以再嵌套children)
children:[
{
//path不再需要加“/”开头
path:"bigbee",
component:BigBee
},
{
path:"smallbee",
component:SmallBee
}
]
}
]
注意:符号@也可表示src/这个目录——@ is an alias to /src
。
<template>
<div id="app">
<router-link to="/bee">Bee主分类</router-link>
<router-view></router-view>
</div>
</template>
<template>
<div>
<h3>演示路由嵌套:蜜蜂分类</h3>
<!--子路径需要写全父路径-->
<router-link to="/bee/bigbee">大</router-link> |
<router-link to="/bee/smallbee">小</router-link>
<!--子路由的路由出口-->
<router-view></router-view>
</div>
</template>
7. 编程式导航
router-link最终是渲染为a标签。
那以编程方式如何实现路由跳转?
<template>
<div>
<h3>大蜜蜂</h3>
<button @click="clickHandler">去首页</button>
</div>
</template>
<script>export default {
methods: {
clickHandler(){
this.$router.push("/")
//this.$router.replace("/")
}
},
}</script>