vue的视图化创建项目_实战Vue简易项目(4)定义视图

本文通过一个Vue简易项目的实战,介绍了如何定义视图,包括视图的组成如NavigationBar和TabBar,以及它们在Layout中的布局。文章详细阐述了在App.vue中局部注册组件、组件的使用限制和局部注册的组件要求。同时,讲解了组件的属性传值、事件绑定、计算属性和方法,以及使用render函数定义组件和Slot的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

视图

包含内容#NavigationBar、#TabBar、#MainContext;

为什么#NavigationBar、#TabBar分在Layout中,而不是components中?

代码上实际上是没有差别的,只是认为#NavigationBar、#TabBar是加载一次的,而非复用,且属于页面布局内容。

App.vue

Vue实例化的根组件,我们在这里进行布局:

src/App.vue文件:

import TabBar from "@/views/layout/TabBar";

import NavigationBar from "@/views/layout/NavigationBar";

export default {

name: 'App',

components: {

'navigation-bar': NavigationBar,

'tab-bar':TabBar,

}

}

...//未动原有样式;

在这里,我们使用标识 其内部的HTML为Vue Template。

内部必有一个且唯一的节点(这里是div#app)包裹内容(即使只是一串字符)-->若存在同级节点,则会报错(这是因为VNode会通过createElement('div')来创建真实节点,只能是单个元素);

通过components属性以键值对的形式引入组件,模板(HTML)中使用的标签名为键名(自定义元素VNode),值为导入的组件模块;

通过components定义组件使用的方式,限制了组件应用的范围。即:如果你在其它文件直接使用,控制台会报错:组件未注册-->这就是组件的局部注册。

局部注册的组件要求:如果在某一文件中应用该组件,必须要使用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

header{

border-bottom: 4px solid #821910;

}

在这里,我们通过style[scoped]定义一份样式,其作用范围仅限于当前文件(又可称模块)模板中的元素。

像下边,在TabBar.vue中的header元素就没有使用到该文件中的对应样式,这就是局部作用域的样式。

局部作用域的样式只对当前文件中的元素起作用,想改变body的样式,不好意思,请全局导入或不使用局部作用域。

src/views/layout/TabBar.vue文件:

测试是否和NavigationBar一样的效果

TabBar

footer{

border-bottom: 4px solid #07776e;

}

显示效果:

NavigationBar

#NavigationBar中分左右结构,左边按钮后退,右边按钮更新页面。

更新页面只是更新数据,而不是整个页面的刷新,每个页面更新数据的接口不同,所以,要作为组件属性传入。

在src/views/layout/navigationBar.vue中:

关闭

刷新

该部分为单文件组件#NavigationBar的Template部分。

@click是v-on:click的简写,用于绑定点击事件。

v-if是Vue中的条件指令,根据返回的布尔值动态添加或移除DOM元素。

/**

* @title:头部标题;

* @refresh:刷新处理函数;

*/

export default {

props: ['title', 'refresh'],

computed: {

hasTitle() {

return this.title && this.title.trim();

},

},

methods: {

goBack() {

this.$router.back()

},

onRefresh() {

this.refresh();

},

},

}

该部分为单文件组件#NavigationBar的组件配置对象。

props为父级(调用该组件的组件)传过来的属性。

传值方式(需要在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的模板:

{{tab.label}}

v-for="tab in tabBars"是Vue中的循环结构,搭配:key使用,优化Vue的渲染机制;

对tabBars进行遍历,tab为数组中的元素。

同样key值,在更新时,会复用组件,而不是销毁后,再创建一个新的组件。

这是是一个新组件的引用。

$parent是组件实例#TabBar的父实例(#App)。

src/views/layout/TabBar.vue组件配置对象:

const tabButton = {

render(createElement) {

return createElement(

'button',

{

"class": this.className,

on: {

[this.event]: this.tabClick,

},

},

this.$slots.default, //指代{{tab.label}}

)

},

props: ['event', 'callBack', 'disabled','el'],

computed: {

className() {

return this.disabled ? 'tab-label disabled' : 'tab-label';

}

},

methods: {

tabClick() {

if (this.disabled) return;

this.callBack && this.callBack(this.el)

}

}

}

export default {

components: {

'tab-button': tabButton

},

props: ['tabBars'],

computed: {

isRender() {

const isRender = _.isArray(this.tabBars) && this.tabBars.length !== 0;

return isRender;

},

},

}

这里使用了另一种方式定义组件tabButton,其与 单文件组件 的区别仅仅在于使用render方法定义模板。

优势:定义出来的组件更具有灵活性,在这里on属性可以动态绑定事件类型。

注意:这里的事件类型[this.event]是作为参数传进来的呢!

组件本质上只是一个JavaScript对象(虚拟DOM),该对象按Vue规定的成员属性构建,区别只在于Template的写作模式。

这里应用了Slot,指代该组件嵌套的子节点。

这里使用了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样式中:

@import "@/styles/mixins.scss";

#app {

@include flex($direction: column);

}

.main-context {

flex-grow: 1;

overflow: hidden;

overflow-y: auto;

}

其中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用于什么情况下呢,有什么好处?

思考

接下来要实现列表了呢,怎么做列表数据呢?

番外

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值