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


一、组件

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:{
    title:{
         type:String, # 包装类
         default:"默认数据"
     },
     num:{
         type:Number, # 包装类
         default:0
     }
}

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"/>
......
data(){
    return{
        helloMessage:""
    }
},
methods: {
	getHelloMessageHandle(data){
      console.log(data);
      this.helloMessage = data;
    }
}
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-router --save

如果在一个模块化工程中使用它,必须要通过 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之外的其他文件中去。具体组织如下
在这里插入图片描述

  • 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 router
  • 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. 路由嵌套

文档结构
在这里插入图片描述

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

那么
this.$router.replace("/") 和 this.$router.push("/")
有什么区别呢?
* replace是替换没有缓存,用户无法返回之前的网页。
* push(形成类似栈的结构)是有缓存的,用户可以返回之前的网页。
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值