浅谈vue 单文件探索


Posted in Javascript onSeptember 05, 2018

在很多Vue项目中,我们使用 Vue.component 来定义全局组件,紧接着用new Vue({ el: '#container '}) 在每个页面内指定一个容器元素。

这种方案在只是使用 JavaScript 增强某个视图的中小型项目中表现得很好。然而在更复杂的项目中,或者当你的前端完全采用 JavaScript 驱动的时候,以下弊端就显现出来:

  • 全局定义(Global definitions) 强制要求每个 component 中的命名不得重复
  • 字符串模板(String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的\
  • 不支持CSS(No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
  • 没有构建步骤(No build step) 限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,如 Pug (formerly Jade) 和 Babel

文件扩展名为 .vue 的 single-file components(单文件组件) 为以上所有问题提供了解决方法,并且还可以使用 Webpack 或 Browserify 等构建工具。

以 vue 作为开发技术栈的前端开发者,往往会配合前端构建工具,进行项目的工程化管理。比如,大家常用的 vue 全家桶 + webpack 的方案进行一些中大型前端项目的开发。配合 webpack 后,vue 的组件化优势更加明显,我们可以通过单文件的组件化开发方式,在工作实践中搭建前端页面,从而提高开发效率。 有这样一个问题:“当我们在写 vue 单文件时,我们在写什么?” 很多人可能会这样回答:template 负责模板,javascript 负责逻辑,style 负责样式。当回答到这里时,一个 vue 开发者的世界观基本上算是很明确了。我们要做的就是在一个单文件组件中写 template、javascript、style。如果仅仅局限于此,显然我们无法从更好的利用的单文件组件服务我们的整个开发流程。接下来我将和大家讨论在 vue 单文件开发中的一些方法论的问题。

vue 单文件本质

vue单文件是以特定文件扩展名 .vue 命名的文件。如下所示的代码:

ListDemo.vue

<template>
  <div class="list-demo">
    <ul>
      <li v-for="item in list" :key="item.key">{{item.value}}</li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'ListNav',
  data() {
    return {
      list: [
        { key: 'home', value: '首页' },
        { key: 'category', value: '文章分类' },
        { key: 'tags', value: '标签' },
        { key: 'about', value: '关于我' },
        { key: 'links', value: '友情链接'},
      ],
    };
  },
};
</script>
<style>
.list-demo {
  font-size: 14px;
}
</style>

代码中含有 template,script,style。三者的作用此处就不在赘述,如上的结构展示了一个 vue 单文件基本的文件结构。其背后的理念就是一个单文件组件对应了一个功能性组件,该组件的模板,样式,业务逻辑都采用就近维护的思想。从组件的复用性,后期可维护性的角度上来说,这样的理念都大大的提高了组件化的开发效率。vue 的单文件,既不是 js,也不是 html,也不是 css 文件,这样的文件如何被应用到页面上,这也就是下面将会说到的一个问题,vue 单文件是如何被处理成页面中可用的资源。

vue 单文件被处理的流程

vue 单文件配合 webpack 构建工具,在 webpack 中会交由 vue-loader 来处理。如下所示:

{
  test: /\.vue$/,
  loader: 'vue-loader',
}

项目中通过 import 或者 require 引入的 vue 单文件,都会经过 vue-loader 处理,vue-loader 在这个过程中会将模板按照 template、script、style 解析并将处理结果返回,三种不同类型的文件交由接下来的loader 进行处理。如果该单文件组件在父组件中的 components 声明,则 components 中对应的该项会被插入解析后 script 代码。这个过程从入口文件 main.js 开始,所有涉及的被依赖单文件组件依次经历这样的处理过程。之后所有的组件的实例化将根据业务逻辑中的依赖关系进行,这个过程也是我们平时在开发中经常用到的一种方式。(这里可以单拉一篇文章详细讲述 vue-loader 的处理流程)

单文件的常用姿势

模板中的组件引用

一、使用方式

组件的拆分和嵌套:

  • 将具体的业务按照功能以及后期复用性方面的考虑划分成更小的组件
  • 通过一个容器组件(父组件)将小的功能组件(子组件)进行整合

操作手法:父组件中引入子组件,components 中注册,template 中添加相应的组件引用模板

这种方式也是我们在进行单文件的开发中常用的一种方式,所有组件的实例化,都被隐含在组件的嵌套关系和业务逻辑中。开发者只需要关心组件的引入,在父组件逻辑中注册该组件,并在父组件的模板中以标签的方式引入组件。这个过程中待引入的组件的实例化时机也可以通过 v-if 指令在业务逻辑中进行控制。

二、适用场景

大部分场景下我们都可以通过这样的方式进行组件化的开发。这种模式的有一个特点: 组件的引入通过组件注册和模板中写入对应的组件的标签来完成。模板中通过标签来引入组件这一步必不可少,这个特点在某些业务场景下可能给开发者带来了一定的重复工作量。

API 式的调用

API 式的调用指的是手动创建子组件的实例,业务逻辑中无需引入组件和模板标签占位,在暴露的 API 中控制组件的实例化与显示。

一、使用方式

  • 功能模块提供一个入口 js 来控制该功能模块下单文件实例的所有功能逻辑
  • 其他组件中使用该功能模块时,调用功能模块下的 js,传入部分参数

操作手法:

Confirm.vue

<template>
  <el-dialg
    title="test"
    :visible.sync="visible">
    {{content}}
    <el-button @click="handleCancelClick">cancel</el-button>
    <el-button @click="handleOkClick">ok</el-button>
  </el-dialg>
</template>
<script>
export default {
  name: 'Confirm',
  data() {
    return {
      visible: false,
      content: '这是一个confirm dialog',
      callback: null,
    };
  },
  methods: {
    handleCancelClick() {
      this.callback('cancel');
    },
    handleOkClick() {
      this.callback('confirm');
    },
  },
};
</script>

confirm.js

import Vue from 'vue';
import Confirm from './confirm';
const ConfirmConstructor = Vue.extend(Confirm);
const confirm = (content) => {
  let confirmInstance = new ConfirmConstructor({
    data: {
      content,
    },
  });
  confirmInstance.vm = confirmInstance.$mount();
  confirmInstance.vm.visible = true;
  // 手动插入目的 dom
  document.body.appendChild(confirmInstance.vm.$el);
  confirmInstance.vm.callback = action => {
    return new Promise((resolve, reject) => {
     resolve(action);
    });
  };
  return confirmInstance.vm;
};

如上所示,给出的是一个确认弹框的场景实现。确认弹框在很多用户交互中是一个必须的交互形式。很多组件库也采用上面这种 API 式的组件调用。调用方仅仅通过 api 的调用,就能实现该功能模块的引用。这样就避免了在 template 中通过标签占位的方式引用。实现原理就是手动接管单文件组件的实例化,通过 Vue.extend 获得该组件对应的 Vue 的子类,在暴露给调用的 api 中去实例化这个组件。这个过程中我们可能还要完成一些组件数据的注入,逻辑相关以及手动将该组件插入到目的 dom 中。手动的注入 dom 是该种方式的一个很大特点,通过在 api 中动态的注入目的 dom,避免我们在各个业务组件中调用该功能模块时重复性的在业务组件 template 中手写组件标签。

二、适用场景

  • 功能聚合度高,组件内逻辑简单,输入输出较为单一,比如一些功能较为独立的弹框
  • 一些特殊的自定义指令开发,比如在一些特殊场景的指令,可以复用一些单文件组件,通过在指令的钩子中实例化组件对应的 vue 子类,按照特定的逻辑插入到目的 dom 中(例如:element-ui的v-loading)

区别和共性

共性:通过实例化对应组件完成组件的功能逻辑

区别:实例化的时机和方式不同。模板式的引入通过组件注册和标签引入的方式来使用单文件组件。标签引入解决了子组件插入的 dom 位置问题,开发者无需关心。API 式的单文件组件使用,在 API 调用时手动实例化组件,需要手动控制插入到目的 dom。

总结

vue 的单文件组件提供了 vue 的组件化开发思路,其本质在导出 vue 的一些关键属性,比如生命周期函数,methods,computed, watch,props等。我们可以通过上述两种方式来使用单文件组件,目的在于工程内部尽量减少重复的模板代码,组件解耦。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript实现拖拽网页内元素的方法
Apr 15 Javascript
前端学习笔记style,currentStyle,getComputedStyle的用法与区别
May 28 Javascript
Javascript之Number对象介绍
Jun 07 Javascript
javascript鼠标滑过显示二级菜单特效
Nov 18 Javascript
javascript垃圾收集机制的原理分析
Dec 08 Javascript
详解Angular 4.x NgTemplateOutlet
May 24 Javascript
用JS实现简单的登录验证功能
Jul 28 Javascript
JavaScript实现图片拖曳效果
Sep 08 Javascript
解决vue v-for 遍历循环时key值报错的问题
Sep 06 Javascript
vue拖拽排序插件vuedraggable使用方法详解
Aug 21 Javascript
JavaScript面试技巧之数组的一些不low操作
Mar 22 Javascript
vue中实现拖动调整左右两侧div的宽度的示例代码
Jul 22 Javascript
快速解决vue动态绑定多个class的官方实例语法无效的问题
Sep 05 #Javascript
jQuery扩展方法实现Form表单与Json互相转换的实例代码
Sep 05 #jQuery
vue中动态添加class类名的方法
Sep 05 #Javascript
ng-events类似ionic中Events的angular全局事件
Sep 05 #Javascript
vue获取元素宽、高、距离左边距离,右,上距离等还有XY坐标轴的方法
Sep 05 #Javascript
vue 监听屏幕高度的实例
Sep 05 #Javascript
Vue-Router的使用方法
Sep 05 #Javascript
You might like
php array的学习笔记
2012/05/16 PHP
PHP setTime 设置当前时间的代码
2012/08/27 PHP
PHP下 Mongodb 连接远程数据库的实例代码
2017/08/30 PHP
PHP fprintf()函数用法讲解
2019/02/16 PHP
Thinkphp 框架配置操作之动态配置、扩展配置及批量配置实例分析
2020/05/15 PHP
javascript 主动派发事件总结
2011/08/09 Javascript
JavaScript Scoping and Hoisting 翻译
2012/07/03 Javascript
JavaScript中使用stopPropagation函数停止事件传播例子
2014/08/27 Javascript
用Jquery.load载入页面后样式没了页面混乱的解决方法
2014/10/20 Javascript
Bootstrap基本模板的使用和理解1
2016/12/14 Javascript
微信端开发--登录小程序步骤
2017/01/11 Javascript
Vue自定义指令使用方法详解
2017/08/21 Javascript
NodeJS如何实现同步的方法示例
2018/08/24 NodeJs
vue使用axios实现excel文件下载的功能
2020/07/16 Javascript
jQuery实现放大镜案例
2020/10/19 jQuery
python使用MySQLdb访问mysql数据库的方法
2015/08/03 Python
Python图算法实例分析
2016/08/13 Python
Python实现的NN神经网络算法完整示例
2018/06/19 Python
解决torch.autograd.backward中的参数问题
2020/01/07 Python
Python开发之身份证验证库id_validator验证身份证号合法性及根据身份证号返回住址年龄等信息
2020/03/20 Python
Lookfantastic挪威官网:英国知名美妆购物网站
2017/07/26 全球购物
参观考察邀请函范文
2014/01/29 职场文书
黄河象教学反思
2014/02/10 职场文书
军训学生自我鉴定
2014/02/12 职场文书
应届毕业生如何写求职信
2014/02/16 职场文书
《生命 生命》教学反思
2014/04/19 职场文书
建材投资建议书
2014/05/16 职场文书
出国签证在职证明
2014/09/20 职场文书
工人先锋号申报材料
2014/12/29 职场文书
采购员岗位职责
2015/02/03 职场文书
医药公司采购员岗位职责
2015/04/03 职场文书
2015年母亲节活动策划方案
2015/05/04 职场文书
黄河绝恋观后感
2015/06/08 职场文书
签证工作证明模板
2015/06/15 职场文书
Python实现的扫码工具居然这么好用!
2021/06/07 Python
Netty客户端接入流程NioSocketChannel创建解析
2022/03/25 Java/Android