文章目录


一、组件

1. 创建组件

VUE2速成-2(组件及路由)_数据


VUE2速成-2(组件及路由)_插槽_02


生成

<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是官方维护的。

VUE2速成-2(组件及路由)_加载_03


官网的参考手册: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>
  • 在src/main.js中配置路由
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之外的其他文件中去。具体组织如下

VUE2速成-2(组件及路由)_vue_04

  • src/router/index.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
  • src/main.js
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')
  • src/App.vue
<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. 路由嵌套

文档结构

VUE2速成-2(组件及路由)_数据_05

  • 定义嵌套路由
    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​​。

  • 定义父级URL跳转
<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>