视图
包含内容#NavigationBar
、#TabBar
、#MainContext
;
为什么#NavigationBar
、#TabBar
分在Layout
中,而不是components
中?
代码上实际上是没有差别的,只是认为#NavigationBar
、#TabBar
是加载一次的,而非复用,且属于页面布局内容。
App.vue
Vue实例化的根组件,我们在这里进行布局:
src/App.vue
文件:
- 在这里,我们使用
<template />
标识 其内部的HTML为Vue Template。 -
<template />
内部必有一个且唯一的节点(这里是div#app
)包裹内容(即使只是一串字符)-->若存在同级节点,则会报错(这是因为VNode会通过createElement('div')来创建真实节点,只能是单个元素); - 通过
components
属性以键值对的形式引入组件,模板(HTML)中使用的标签名为键名(自定义元素VNode),值为导入的组件模块; - 通过
components
定义组件使用的方式,限制了组件应用的范围。即:如果你在其它文件直接使用<navigation-bar></navigation-bar>
,控制台会报错:组件未注册-->这就是组件的局部注册。 - 局部注册的组件要求:如果在某一文件中应用该组件,必须要使用
components
注册一次。 - 导入组件
import TabBar from "@/views/layout/TabBar";
路径以@
起,这是因为build/webpack.base.conf.js
中配置的路径别名'@' === "resolve('src')"
:
resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), //可以追加当前项目下,想快捷访问的文件目录 } },
临时定义的组件文件:
src/views/layout/NavigationBar.vue
文件:
NavigationBar
- 在这里,我们通过
style[scoped]
定义一份样式,其作用范围仅限于当前文件(又可称模块)模板中的元素。 - 像下边,在
TabBar.vue
中的header
元素就没有使用到该文件中的对应样式,这就是局部作用域的样式。 - 局部作用域的样式只对当前文件
<template/>
中的元素起作用,想改变body
的样式,不好意思,请全局导入或不使用局部作用域。
src/views/layout/TabBar.vue
文件:
测试是否和NavigationBar一样的效果
显示效果:
NavigationBar
#NavigationBar
中分左右结构,左边按钮后退,右边按钮更新页面。
更新页面只是更新数据,而不是整个页面的刷新,每个页面更新数据的接口不同,所以,要作为组件属性传入。
在src/views/layout/navigationBar.vue
中:
{ {title}}
- 该部分为单文件组件
#NavigationBar
的Template部分。 -
@click
是v-on:click
的简写,用于绑定点击事件。 -
v-if
是Vue
中的条件指令,根据返回的布尔值动态添加或移除DOM元素。
- 该部分为单文件组件
#NavigationBar
的组件配置对象。 -
props
为父级(调用该组件的组件)传过来的属性。-
传值方式
<navigation-bar title="我是标题" :refresh="refresh"></navigation-bar>
(需要在src/App.vue
中定义refresh
函数)-
title
传的值为字符串,不需要:
前缀; -
:refresh
传的值为非字符串(数字、布尔值、函数、数组、对象...),:
为前缀,值为Javascript表达式计算结果;
-
- 在程序中,如
this.title
引用props
的值。 - 在模板中,作元素的innerHTML内容时,如
{ {title}}
引用。
-
-
methods
为该组件内,元素绑定的事件处理函数。- 在程序中,如
this.refresh()
引用。 - 在模板中,如
@click="onRefresh"
调用,传入的是函数应用;若传参,如@click="onRefresh(param)"
调用。
- 在程序中,如
-
computed
本身写法和函数定义一致,然而,其本身是一个data
(数据源),字段名为函数名,值为函数的返回值。- 使用方式与
props
一致。
- 使用方式与
区别 | method | computed |
---|---|---|
类型 | 函数 | 数据变量 |
参数 | 可以带参 | 不带参(非函) |
触发 | 交互时触发 | 声明内部的this属性的值变化时执行 |
显示效果
这里样式请大家随意设定,我使用的是flexBox布局。
点击刷新,我定义了console.log('refresh success')
。
TabBar
#TabBar
分以下情况:
- 无
- 一个按钮
- 两个按钮
每个视图中#TabBar
按钮是不同的,所以,按钮的配置要当作组件属性传入(控制变化的量)。
测试数据源
const tabBars = [ { label: '提交', eventType: 'click', disabled: false, callBack(vm) { console.log('单击,提交'); } }, { label: '取消', eventType: 'dblclick', //该事件在手机模式下无法响应呢,只能在PC模式下调试 disabled: false, callBack(vm) { console.log('双击,取消'); } }]
src/views/layout/TabBar.vue
的模板:
-
v-for="tab in tabBars"
是Vue
中的循环结构,搭配:key
使用,优化Vue
的渲染机制;- 对
tabBars
进行遍历,tab
为数组中的元素。 - 同样
key
值,在更新时,会复用组件,而不是销毁后,再创建一个新的组件。
- 对
-
<tab-button :el="$parent" :disabled="tab.disabled" :event="tab.eventType" :callBack="tab.callBack">
这是是一个新组件的引用。-
$parent
是组件实例#TabBar
的父实例(#App
)。
-
src/views/layout/TabBar.vue
组件配置对象:
-
这里使用了另一种方式定义组件
tabButton
,其与 单文件组件 的区别仅仅在于使用render
方法定义模板。-
优势:定义出来的组件更具有灵活性,在这里
on
属性可以动态绑定事件类型。- 注意:这里的事件类型
[this.event]
是作为参数传进来的呢!
- 注意:这里的事件类型
- 组件本质上只是一个JavaScript对象(虚拟DOM),该对象按
Vue
规定的成员属性构建,区别只在于Template
的写作模式。
-
- 这里应用了,指代该组件嵌套的子节点。
- 这里使用了
underscore.js
(_.isArray
),需要在build/webpack.base.conf.js
中配置:
const webpack = require('webpack');...module.exports = { ... module:{ ... }, plugins:[ new webpack.ProvidePlugin({ _: 'underscore', }), ], ...
然后,underscore
在全局可用。
因为这里的配置对dev
和prod
环境是一致的,所以,直接在build/webpack.base.conf.js
中配置了。
显示效果
整体Layout布局
最终,我们要做一个顶天立地的内滚动结构(使用flexBox布局即可):
src/App.vue
样式中:
其中src/styles/mixins.scss
:
@mixin flex($direction:row, $alignItems: stretch, $justifyContent: space-between, $basis: auto,$wrap:nowrap) { display: flex; flex-direction: $direction; align-items: $alignItems; justify-content: $justifyContent; flex-basis: $basis; flex-wrap: $wrap;}
章节回顾
- 我这里面省略了将写好的
#NavigationBar
、#TabBar
替换原临时搭建的对应组件,我相信你能处理好,对吧?! -
#App
小节中,是怎样注册局部组件的,如果想要在项目所有模板中可以直接使用标签名来应用组件,该怎么处理呢? -
#App
小节中,如何定义局部样式的,如果想让app.vue
中header的样式全局可用,该怎么处理呢? - 父组件如何传值给子组件,若想传布尔值,该如何操作?
-
render
函数如何渲染组件模板,使用该方法如何定义组件? -
slot
用于什么情况下呢,有什么好处?
思考
- 接下来要实现列表了呢,怎么做列表数据呢?