系列文章目录
上期我们讲解了v-model等等的一些指令,这期我们来讲解组件方面的内容
文章目录
1、组件
重复的页面结构,数据,逻辑 都可以抽提成一个组件
- 特点 简单 高效 不重复
- 组件和实例相似之处: data/methods/computed/watch 等一应俱全
注意: - data和Vue实例的区别为
- 组件中data为一个函数且需要返回一个对象
- 组件没有el选项
- template 代表其 页面结构 (有且只要一个根元素)
每个组件都是 独立 的 运行作用域、数据、逻辑没有任何关联
1.1、全局组件
全局和局部: 注册方式不同 应用范围不同
注意: 注意命名规范
路径: 实现一个全局组件
- 定义一个全局组件
- 写入组件选项
- 使用组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app" v-cloak>
<content-a></content-a>
</div>
<script src="./vue.js"></script>
<script>
Vue.component(
"content-a",
{
template: `
<div>
<button @click="add">加</button>
<span>{{count}}</span>
<button @click="cut">减</button>
</div>
`,
data(){
return{
count: 1
}
},
methods: {
add(){
this.count++
},
cut(){
this.count--
}
}
}
)
let vm = new Vue({
el: '#app',
data: {
msg:'ok,ok',
},
component:{
},
methods: {
fn(e) {
//msg=最新的value
this.msg = e.target.value
}
},
filters:{
filter01:function (vl) {
return vl+'filter01'
},
filter02:function (vl) {
return vl+'filter02'
},
capita : function (value) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
},
directives: {
"focus": {
inserted(dom) {
dom.focus();
}
}
}
})
</script>
</body>
</html>
上述代码时实现对count的自加自减,也是对全局过滤器的演示
1.2、局部组件
局部组件的实现
- 在实例选项compoents中定义局部组件名字
- 在组件名字相对应的对象中定义选项(template、data()、…)
- 在实例视图中使用组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app" v-cloak>
<content-a></content-a>
<content-b></content-b>
</div>
<script src="./vue.js"></script>
<script>
Vue.component(
"content-a",
{
template: `
<div>
<button @click="add">加</button>
<span>{{count}}</span>
<button @click="cut">减</button>
</div>
`,
data(){
return{
count: 1
}
},
methods: {
add(){
this.count++
},
cut(){
this.count--
}
}
}
)
let vm = new Vue({
el: '#app',
data: {
msg:'ok,ok',
},
components:{
'content-b':{
template: `
<div>
<button @click="add">加</button>
<span>{{count}}</span>
<button @click="cut">减</button>
</div>
`,
data(){
return{
count: 1
}
},
methods: {
add(){
this.count++
},
cut(){
this.count--
}
}
}
},
methods: {
fn(e) {
//msg=最新的value
this.msg = e.target.value
}
},
filters:{
filter01:function (vl) {
return vl+'filter01'
},
filter02:function (vl) {
return vl+'filter02'
},
capita : function (value) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
},
directives: {
"focus": {
inserted(dom) {
dom.focus();
}
}
}
})
</script>
</body>
</html>
1.3、组件的嵌套
- 全局组件 嵌套 全局组件
- 局部组件 嵌套 全局组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app" v-cloak>
<content-c></content-c>
</div>
<script src="./vue.js"></script>
<script>
Vue.component(
"content-a",
{
template: `
<div>
<button @click="add">加</button>
<span>{{count}}</span>
<button @click="cut">减</button>
</div>
`,
data(){
return{
count: 1
}
},
methods: {
add(){
this.count++
},
cut(){
this.count--
}
}
}
)
let vm = new Vue({
el: '#app',
data: {
msg:'ok,ok',
},
components:{
'content-b':{
template: `
<div>
<button @click="add">加</button>
<span>{{count}}</span>
<button @click="cut">减</button>
</div>
`,
data(){
return{
count: 1
}
},
methods: {
add(){
this.count++
},
cut(){
this.count--
}
}
},
'content-c':{
template: `
<content-a></content-a>
`,
data(){
return{
count: 1
}
},
methods: {
add(){
this.count++
},
cut(){
this.count--
}
}
}
},
methods: {
fn(e) {
//msg=最新的value
this.msg = e.target.value
}
},
filters:{
filter01:function (vl) {
return vl+'filter01'
},
filter02:function (vl) {
return vl+'filter02'
},
capita : function (value) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
},
directives: {
"focus": {
inserted(dom) {
dom.focus();
}
}
}
})
</script>
</body>
</html>
1.4、组件间的通信
组件嵌套 => 父子组件 => 父组件传递数据给子组件使用 => 组件之间的传值 => 也叫组件之间的通信
组件之间的通信根据关系的可以分为:
- 父子组件通信
父组件到子组件
子组件到父组件 - 兄弟组件通信
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 子组件 -->
<child-a :msg="msgP"></child-a>
</div>
<script src="./vue.js"></script>
<script>
Vue.component('child-a', {
template: `
<div>
我是子组件{{count}}
{{msg}}
</div>
`,
data() {
return {
count: 100
}
},
props: ['msg']
})
let vm = new Vue({
el: '#app',
data: {
msgP: '我是父组件'
}
})
</script>
</body>
</html>
2、路由
组件和模块的区别
- 模块:侧重于功能或者数据的封装
- 组件:包含了 template、style 和 script,而它的 script 可以由各种模块组成
单页应用(简称SPA)
- 传统模式 每个页面及其内容都需要从服务器一次次请求 如果网络差, 体验则会感觉很慢
- SPA模式, 第一次 加载 会将所有的资源都请求到页面 模块之间切换 不会再请求服务器
SPA优点:
- 用户体验好,因为前端操作几乎感受不到网络的延迟
- 完全组件化开发 ,由于只有一个页面,所以原来属于一个个页面的工作被归类为一个个 组件
SPA缺点:
- 首屏 加载慢(可以只加载所需部分)
- 不利于 SEO ( 服务端渲染 可以解决)
- 开发难度高 (框架)
单页应用 SPA-实现原理
- 可以通过页面地址的锚链接来实现spa
- hash(锚链接)位于链接地址 # 之后
- hash值的改变 不会触发 页面刷新
- hash值是url地址的一部分,会存储在页面地址上 我们可以获取到
- 可以通过 事件监听 hash值得改变
- 拿到了hash值,就可以根据不同的hash值进行不同的 内容切换
2.1、基于js路由的实现
通过上一个小节内容可以得出 采用 hash值改变 的特性来进行前端路由切换
- 实现导航结构(’#/aaa’)
- onhashchange事件监听hash值的改变
- 获取hash值 根据值的不同 改变视图内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li><a href="#/aaa">aaa</a></li>
<li><a href="#/bbb">bbb</a></li>
<li><a href="#/ccc">ccc</a></li>
<li><a href="#/ddd">ddd</a></li>
</ul>
<!-- 容器 -->
<div id="main"></div>
<script>
// 根据不同内容渲染不同的标识
let oBox = document.getElementById('main')
window.onhashchange = function () {
// 获取hash值
let hash = location.hash
console.log(hash)
hash = hash.replace('#/', '')
switch (hash) {
case "aaa":
oBox.innerText = 'AAA'
break
case "bbb":
oBox.innerText = 'BBB'
break
case "ccc":
oBox.innerText = 'CCC'
break
case "ddd":
oBox.innerText = 'DDD'
break
default:
break
}
}
</script>
</body>
</html>
2.2、vue-router实现
- Vue-Router 是 Vue.js 官方的路由管理器。
- 它和 Vue.js 的核心深度集成,让构建单页面应用变得简单
- 实现根据不同的请求地址 而 显示不同的内容
- 如果要使用 vue开发项目,前端路由功能 必须使用 vue-router来实现
需要引入js文件:
- 可以npm
- cdn
- 也可以直接下载导入
基本语法:
- 导入vue和vue-router
- 设置HTML中的内容
- 实例化路由对象,配置路由规则
- 创建路由对应的组件
- 把router实例挂载到vue实例上
2.设置HTML中的内容
<!-- router-link 最终会被渲染成a标签,to指定路由的跳转地址 -->
<router-link to="/users">用户管理</router-link>
<router-link to="/home">首页展示</router-link>
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
// 3.配置路由规则
var router = new VueRouter({
routes: [
{ path: '/users', component: Users }
{ path: '/home', component: Home }
]
});
// 4.创建组件
let Home = {
template: '<div>这是Home内容</div>'
};
let Users = {
template: '<div>这是用户管理内容</div>'
};
// 5.把router实例挂载到vue实例上
var vm = new Vue({
el: '#app',
router
});
2.3、代码实现vue-router
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 配置路径 -->
<router-link to="/home">主页</router-link>
<router-link to="/top">热点</router-link>
<router-link to="/abouts">关于我们</router-link>
<!-- 显示的内容 -->
<router-view></router-view>
</div>
<script src="./vue.js"></script>
<script src="./vue-router.js"></script>
<script>
// 模板内容
let Home = {
template: `<div><span>主页</span></div>`
}
let Top = {
template: `<div><span>热点</span></div>`
}
let Abouts = {
template: `<div><span>关于我们</span></div>`
}
// 匹配规则
let router = new VueRouter({
routes: [{
path: '/home',
component: Home
}, {
path: '/top',
component: Top
}, {
path: '/abouts',
component: Abouts
}]
})
let vm = new Vue({
el: '#app',
data: {},
router
})
</script>
</body>
</html>
2.4、实现动态路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./vue.js"></script>
<script src="./vue-router.js"></script>
</head>
<body>
<div id="dianshi">
<router-link to="/item/8">看什么看这是我的小米电视</router-link>
<router-link to="/item/9">看什么看这是我的华为电视</router-link>
<router-view></router-view>
</div>
<script>
let home = {
template: `<div>我是home{{$route.params.id}}</div>`,
}
let router = new VueRouter({
routes: [{
path: '/item/:id',
component: home
}]
})
let vue = new Vue({
el: '#dianshi',
data: {
msg: 'hello'
},
router
})
</script>
</body>
</html>
2.5、to属性
to有很多赋值方式
<!-- 常规跳转 -->
<!-- <router-link to="/aaa">aaa</router-link> -->
<!-- 变量 -->
<!-- <router-link :to="bbb">bbb</router-link> -->
<!-- 根据对象name跳转 --> (注意:name值是字符串)
<!-- <router-link :to="{name:'ccc'}">ccc</router-link> -->
<!-- 根据对象path跳转 -->(注意:必须得加上/ 不然容易错乱)
<!-- <router-link :to="{path:'/ddd'}">ddd</router-link> -->
<!-- 带参数的跳转 --> (注意获取参数route 不要写成router)
<!--<router-link :to="{name:'eee',params:{id:1}}">体育</router-link> -->
<router-view></router-view>
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 配置路径 -->
<router-link to="/home">主页</router-link>
<router-link :to="top">热点</router-link>
<router-link :to="{path:'/abouts'}">关于我们</router-link>
<router-link :to="{name:'aaa'}">AAA</router-link>
<router-link :to="{name:'bbb',params:{id:300}}">BBB</router-link>
<!-- 显示的内容 -->
<router-view></router-view>
</div>
<script src="./vue.js"></script>
<script src="./vue-router.js"></script>
<script>
// 模板内容
let Home = {
template: `<div><span>主页</span></div>`
}
let Top = {
template: `<div><span>热点</span></div>`
}
let Abouts = {
template: `<div><span>关于我们</span></div>`
}
let AAA = {
template: `<div><span>AAA</span></div>`
}
let BBB = {
template: `<div><span>BBB{{$route.params.id}}</span></div>`
}
// 匹配规则
let router = new VueRouter({
routes: [{
path: '/home',
component: Home
}, {
path: '/top',
component: Top
}, {
path: '/abouts',
component: Abouts
}, {
name: 'aaa',
path: '/aaa',
component: AAA
}, {
name: 'bbb',
path: '/bbb/:id',
component: BBB
}]
})
let vm = new Vue({
el: '#app',
data: {
top: '/top'
},
router
})
</script>
</body>
</html>
3、重定向
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 配置路径 -->
<router-link to="/home">主页</router-link>
<router-link to="/top">热点</router-link>
<router-link to="/abouts">关于我们</router-link>
<!-- 显示的内容 -->
<router-view></router-view>
</div>
<script src="./vue.js"></script>
<script src="./vue-router.js"></script>
<script>
// 模板内容
let Home = {
template: `<div><span>主页</span></div>`
}
let Top = {
template: `<div><span>热点</span></div>`
}
let Abouts = {
template: `<div><span>关于我们</span></div>`
}
// 匹配规则
let router = new VueRouter({
routes: [{
path: '/home',
component: Home
}, {
path: '/top',
component: Top,
redirect:'/home'
}, {
path: '/abouts',
component: Abouts
}]
})
let vm = new Vue({
el: '#app',
data: {},
router
})
</script>
</body>
</html>
4、编程式导航
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 配置路径 -->
<router-link to="/home">主页</router-link>
<router-link :to="top">热点</router-link>
<router-link :to="{path:'/abouts'}">关于我们</router-link>
<router-link :to="{name:'aaa'}">AAA</router-link>
<router-link :to="{name:'bbb',params:{id:300}}">BBB</router-link>
<button @click="fnclick">编程式导航</button>
<!-- 显示的内容 -->
<router-view></router-view>
</div>
<script src="./vue.js"></script>
<script src="./vue-router.js"></script>
<script>
// 模板内容
let Home = {
template: `<div><span>主页</span></div>`
}
let Top = {
template: `<div><span>热点</span></div>`
}
let Abouts = {
template: `<div><span>关于我们</span></div>`
}
let AAA = {
template: `<div><span>AAA</span></div>`
}
let BBB = {
template: `<div><span>BBB{{$route.params.id}}</span></div>`
}
// 匹配规则
let router = new VueRouter({
routes: [{
path: '/home',
component: Home
}, {
path: '/top',
component: Top,
redirect: '/home'
}, {
path: '/abouts',
component: Abouts
}, {
name: 'aaa',
path: '/aaa',
component: AAA
}, {
name: 'bbb',
path: '/bbb/:id',
component: BBB
}]
})
let vm = new Vue({
el: '#app',
data: {
top: '/top'
},
methods: {
fnclick() {
this.$router.push({
name: 'aaa'
})
}
},
router
})
</script>
</body>
</html>
5、路由激活样式
当前路由在导航中是拥有激活class样式的
<a href="#/bj" class="router-link-exact-active router-link-active">北京</a>
总结
这里我们主要讲了路由、组件等等方面的内容、下一期我们讲解动画方面的知识。